接觸 dubbo 有一段時間,特別想拿 dubbo 和 tomcat 比較一番。
tomcat 是 web 服務器,提供 http 服務,當 tomcat 收到瀏覽器發送的 http 請求時,根據 url 查詢對應的 servlet 處理請求,然后發送 http 響應。
dubbo 是 rpc 框架,提供 dubbo 服務,當 provider 收到 consumer 發送的請求后,解析請求,找到對應的接口服務類(原始接口服務類的外面裹着代理和一系列 filter),處理請求,發送響應。
初看上去,很相同,仔細一想,卻也大不同。
相同點:
tomcat 是 http 服務器,基於 tcp 協議,dubbo 框架支持多種協議(dubbo, hessian 等),常用的 dubbo 協議也是基於 tcp 協議。即 dubbo 的 consumer 和 provider 首先要建立 tcp 連接,然后才能發送數據。
不同點:
(1)http 客戶端如何發送請求?首先建立 tcp 連接,然后發送 http 請求報文,接着等待 http 響應報文,http 報文都是明文字符串。
dubbo 客戶端如何發送請求呢?正常情況下,provider 會暴露服務,consumer 去 refer 服務獲得代理,然后通過代理調用服務,這里有很長的一個調用棧,底層也是 consumer 發送 request,等待 provider 的 response,但是這里的 request 和 response 都是序列化的 java 對象。
(2)http 客戶端發送請求,必然會收到對應的 http 響應,而 dubbo consumer 發送請求時可以設置為 oneway,即不需要響應,則 dubbo provider 不會發送響應,請求也可以設置為同步或者異步,不管同步還是異步都是有響應的。
最后看看,dubbo provider 如何根據請求定位到具體的服務實現類,調用棧如下圖:

Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException{ boolean isCallBackServiceInvoke = false; boolean isStubServiceInvoke = false; int port = channel.getLocalAddress().getPort(); String path = inv.getAttachments().get(Constants.PATH_KEY); //如果是客戶端的回調服務. isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY)); if (isStubServiceInvoke){ port = channel.getRemoteAddress().getPort(); } //callback isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke; if(isCallBackServiceInvoke){ path = inv.getAttachments().get(Constants.PATH_KEY)+"."+inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY); inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString()); } // serviceKey = "com.zhang.HelloService:20880" String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.GROUP_KEY)); DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey); if (exporter == null) throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + inv); return exporter.getInvoker(); }
主要是根據 serviceKey 查找 exporter。
