1. 程式人生 > >dubbo原始碼詳細分析

dubbo原始碼詳細分析

一.總體
(一).分層架構:http://dubbo.apache.org/books/dubbo-dev-book/design.html
核心 registry(註冊消費者)->cluster(叢集處理)->dubbo.rpc(代理封裝格式)->remoting(遠端網路傳輸)
詳細的從上到下依次為service,config,proxy,registry,cluster,monitor,protocol,,exchange,transport,
serialize模組
(二).採用Microkernel + Plugin模式,Microkernel只負責組裝Plugin,Dubbo自身的功能也是通過擴充套件點實現.
在擴充套件類的jar包內,放置擴充套件點配置檔案:META-INF/dubbo/介面全限定名,內容為:配置名=擴充套件實現類全限定名,多個實現類用換行符分隔.
(三).生產者和spring結合
spring載入dubbo過程。
由於spring例項化的ServiceBean是單例模式的,在Spring的容器ApplicationContext的啟動過程refresh過程中最後第二步會預先初始化單例的bean, 在bean的初始化過程會設定beanName,設定容器applicationContext,回撥InitializingBean的afterPropertiesSet。最後一步finishRefresh會觸發ContextRefreshedEvent事件, 而ServiceBean實現了ApplicationListener介面監聽了此事件, 而在之前一步例項化的ServiceBean註冊了這個事件,所以ServiceBean的onApplicationEvent(ApplicationEvent event)方法被觸發, 在這個方法中觸發了export方法來暴露服務。
(四).領域概念
1.服務提供端:ServiceConfig->ProxyFactory(JavassistProxyFactory或者JdkProxyFactory)->Invoker(AbstractProxyInvoker的例項)->filter(exception,monitor等)->RegistryProtocol(Dubbo,Hessian,Injvm,Rmi,WebService等)->exporter->server->transporter->serializtion->codec(telnet,transport,exchange)
2.消費呼叫端:jdk dynamic proxy->cluster->directory->registry->router->loadbalance->filter(monitor等)->invoker->client->transporter->codec

二.服務端程式碼流程
(二.0)先在匯出服務的service的bean配置中注入ProviderConfig(threadPool=cached),
ApplicationConfig(dubbo.application.environment=test/develop/product, dubbo.application.provider.name=linglongta.test.provider, dubbo.monitor.isNeed=notNeed),
MonitorConfig(dubbo.monitor.address.port=10.168.163.241:7070),
RegistryConfig(dubbo.registry.address=10.168.163.241,dubbo.registry.port=2811,dubbo.registry.protocol=zookeeper),
ProtocolConfig(dubbo.application.protocol=dubbo,dubbo.protocol.port=-1).
在每個介面中,設定interface,group(dubbo.provider.group=testLingLongTaGroup),application(前面的ApplicationConfig),protocol(前面的ProtocolConfig),
registry(前面的RegistryConfig),ref(注入的service實現),timeout(dubbo.provider.timeout=300000).
(二.1)Config層
1.由spring進入com.alibaba.dubbo.config.ServiceConfig.export()(消費端的有一個類似的ReferenceConfig類)函式,獲取前面ProviderConfig的export屬性的布林值,判斷是否暴露,不暴露的話直接返回。獲取前面ProviderConfig的delay屬性的整數值,如果需要delay,則delay幾秒在呼叫doExport.否則,立即呼叫doExport.進入函式com.alibaba.dubbo.config.ServiceConfig.doExport(),
com.alibaba.dubbo.config.ServiceConfig.doExport()函式過程:
1.1設定exported為true,防止重複進入匯出函式匯出這個服務。判斷interfaceName是否為空,為空拋異常。懷疑interfaceName是ServiceConfig的建構函式成通過模板com.alibaba.dubbo.config.annotation.Service設定的??
1.2進入函式com.alibaba.dubbo.config.AbstractConfig.appendProperties(AbstractConfig),把上面的ProviderConfig呼叫方法com.alibaba.dubbo.config.AbstractConfig.appendProperties(AbstractConfig)複製到屬性ServiceConfig.provider中,具體邏輯是讀取provider中的xml中的配置,比如AbstractConfig可能未registry,protocol,application中的一種,設定到屬性中.
1.3設定provider的ApplicationConfig為ServiceConfig的application屬性,設定provider的ModuleConfig為ServiceConfig的module屬性,設定provider的List的RegistryConfig為ServiceConfig的registries屬性,設定provider的MonitorConfig為ServiceConfig的monitor屬性,設定provider的list的ProtocolConfig為ServiceConfig的protocols屬性,
1.4如果dubbo介面的實現類ref屬於com.alibaba.dubbo.rpc.service.GenericService型別,代表泛化呼叫,設定為interfaceClass為GenericService型別。否則,通過反射設定interfaceClass為呼叫dubbo的interface介面型別.檢查配置的MethodConfig中的方法是否都在該interface中存在,不存在則報異常。呼叫com.alibaba.dubbo.config.ServiceConfig.checkRef()檢查dubbo的bean物件必須實現了bean的interface介面.如果服務介面的本地實現類名stub不為空,檢查它是否實現或者繼承了介面.
1.5.做dubbo的舊版本ApplicationConfig相容。做dubbo的舊版本RegistryConfig相容。做dubbo的舊版本ProtocolConfig相容。
2.呼叫com.alibaba.dubbo.config.ServiceConfig.doExportUrls()方法匯出URL介面。
2.1呼叫com.alibaba.dubbo.config.AbstractInterfaceConfig.loadRegistries(boolean)遍歷所有的RegistryConfig,目前已知的註冊中心只有zookeeper好像。每次遍歷時,獲取註冊中心的ip地址列表,path,dubbo版本,時間戳,protocol,將這些變數put到map中,呼叫com.alibaba.dubbo.common.utils.UrlUtils.parseURLs()根據ip地址的個數轉成對應個數的URL字串的列表。
2.2迴圈遍歷不同的ProtocolConfig,比如dubbo,rmi,http,hessian,injvm等,目前基本都是dubbo.進入函式com.alibaba.dubbo.config.ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig, List<URL>).該函式邏輯:連線所有的地址列表,看是否可以連線。建立map,放入dubbo的version,時間戳,application,module,protocolConfig,interface,methods,ip等,這些可以用來生產URL.遍歷所有方法配置,看引數型別是否匹配?根據配置,匯出URL的服務。
2.3 遍歷所有地址列表,配置的了monitor載入monitor,並給URL設定MONITOR_KEY.根據服務具體實現,實現介面以及regitryUrl從代理工廠ProxyFactory獲取代理Invoker(繼承於AbstractProxyInvoker),通過方法com.alibaba.dubbo.rpc.ProxyFactory.getInvoker(T, Class<T>, URL)實現,它是對具體實現的一種代理.預設使用javassist庫做反射.此處重要!!!進入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker()獲取Invoker.只是獲取新的Invoker,不會呼叫它的com.alibaba.dubbo.common.bytecode.Wrapper.invokeMethod()方法.
2.4呼叫com.alibaba.dubbo.rpc.Protocol.export(Invoker<?>)暴露出RegistryProtocol的export()函式。
(二.2)RegistryProtocol層(以dubboProtocol為例,有此層存在的目的是還有dubbo,hessian,memche,redis,thrift等協議)
進入com.alibaba.dubbo.registry.integration.RegistryProtocol.export(Invoker<T>).這步會呼叫com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain()建立Filter鏈. 在此方法中呼叫com.alibaba.dubbo.registry.RegistryService.register(),進入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.ZookeeperRegistry()建立zookeeper服務節點。在com.alibaba.dubbo.registry.support.AbstractRegistry.AbstractRegistry(URL)會快取檔案,服務提供方的地址.然後呼叫函式com.alibaba.dubbo.registry.integration.RegistryProtocol.doLocalExport()暴露服務。步驟是進入函式com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.export(Invoker<T>).根據invoker的url生成cacheKey(格式為com.alibaba.dubbo.demo.DemoService:20880).根據invoker,cacheKey,exporterMap生成DubboExporter,主要是把前面的三個欄位作為exporter的屬性存起來.從URL中判斷是否是代理支援的事件,是否是回撥服務。呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.openServer(URL)方法根據Url建立一個Server,serverMap的key格式為(192.168.2.242:20880).預設使用netty,dubbo協議,我理解加入Exchanger的目的是除了Netty,可以擴充套件為其他網路框架,比如支援http,mina,grizzly,p2p,netty4,zookeeper等,類似mvc的分層隔離。然後依次進入dubbo-remoting層的com.alibaba.dubbo.remoting.exchange.Exchangers.bind(),com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchanger.bind(),com.alibaba.dubbo.remoting.transport.netty.NettyTransporter.bind().加入Transporter的目的是區分Grizzly,Netty,Mina等的NIO框架.出入的回撥處理類為com.alibaba.dubbo.remoting.transport.DecodeHandler.dubbo在暴露服務最終開啟的netty服務監聽,監聽消費者傳送的請求,通過反射呼叫方法得到結果通過 tcp/ip 網路傳輸返回給消費者。在com.alibaba.dubbo.remoting.transport.netty.NettyServer.NettyServer()的構造方法中,進入方法com.alibaba.dubbo.remoting.transport.netty.NettyServer.doOpen()開啟伺服器。
1.Netty監聽.進入函式com.alibaba.dubbo.remoting.transport.AbstractServer.AbstractServer(URL, ChannelHandler)->com.alibaba.dubbo.remoting.transport.netty.NettyServer.doOpen().建立server監聽.具體傳送,接受訊息報文的方法在com.alibaba.dubbo.remoting.transport.netty.NettyHandler類中。
2. 如前所述,開啟netty服務是在RegistryProtocol.export 的doLocalExport中,在開啟了netty服務後.在RegistryProtocol.export中呼叫com.alibaba.dubbo.registry.RegistryService.subscribe(URL, NotifyListener)進入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doSubscribe(URL, NotifyListener)方法在zookeeper上註冊服務節點了,消費者在消費服務時會根據消費的介面名找到對應的zookeeper節點目錄,對目錄進行監聽,接收推送.常用註冊中心實現有 dubbo、Redis、zookeeper,這也是在我們配置時經常看到的 註冊協議的配置 ,最為常用的就是 zookeeper.在zookeeper上面建立節點,預設不分組的情況下,服務結構如下:provider://192.168.2.242:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=8132&side=provider&timestamp=1525425383005.當zk上節點變化時進入com.alibaba.dubbo.registry.support.FailbackRegistry.notify(URL, NotifyListener, List<URL>).還會註冊/dubbo/com.alibaba.dubbo.demo.DemoService/(configurators, consumers, providers, routers)節點.

三.消費端程式碼流程
(三.1)消費端初始化(主要是根據xml配置生成url,再生成invoker物件)
消費者初始化時主要做的事情就是引用對應的遠端服務,執行了以下步驟:監聽註冊中心,連線服務提供端,建立消費端服務代理.
比如飛俠中的cn.shibei.feixia.remote.dubbo.ShibeiRemoteServiceConfig這種初始化消費者的方式。流程是.
1.呼叫org.springframework.beans.factory.support.AbstractBeanFactory.getBean(),進入com.alibaba.dubbo.config.ReferenceConfig.get(),進入com.alibaba.dubbo.config.ReferenceConfig.init()(服務端是ServiceConfig)初始化主要目的是讀取xml中的dubbo相關配置放置到ReferenceConfig的屬性中,放置到一個map中.步驟是:通過反射生成interface的介面物件,比如interface com.alibaba.dubbo.demo.DemoService.進入com.alibaba.dubbo.config.ReferenceConfig.createProxy(Map),引數是前面的map,內容比如{side=consumer, application=demo-consumer, register.ip=192.168.11.190, methods=sayHello, qos.port=33333, dubbo=2.0.0, pid=3100, check=false, interface=com.alibaba.dubbo.demo.DemoService, timestamp=1525500374971}.先呼叫com.alibaba.dubbo.config.AbstractInterfaceConfig.loadRegistries()根據xml中的註冊中心的配置,比如[<dubbo:registry address="127.0.0.1:4182,127.0.0.1:4180,127.0.0.1:4181" protocol="zookeeper" id="com.alibaba.dubbo.config.RegistryConfig" />]生成消費端的URL物件,比如[registry://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&pid=6676&qos.port=33333&registry=zookeeper&timestamp=1525502765902].
2.在createProxy中然後依次呼叫ProtocolListenerWrapper->ProtocolFilterWrapper->RegistryProtocol例項的refer方法->com.alibaba.dubbo.registry.support.AbstractRegistryFactory.getRegistry(URL),URL格式為zookeeper://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=6676&qos.port=33333&timestamp=1525502765902.然後呼叫com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory.createRegistry(URL), new 一個ZookeeperRegistry()物件返回,在構造方法中初始化zk連線,呼叫zkClient連線zookeeper://127.0.0.1:4182/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&backup=127.0.0.1:4180,127.0.0.1:4181&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=6676&qos.port=33333&timestamp=1525502765902.
3.在RegistryProtocol.refer(Class<T>, URL)中繼續呼叫com.alibaba.dubbo.registry.integration.RegistryProtocol.doRefer(Cluster, Registry, Class<T>, URL).
在方法中new一個RegistryDirectory物件,屬性為Registry,Protocol?在方法中呼叫com.alibaba.dubbo.registry.support.FailbackRegistry.register(URL),引數是consumer://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637.呼叫com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doRegister(URL),呼叫zkClient.create把消費者註冊到註冊中心,註冊的節點為/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer%3A%2F%2F192.168.11.190%2Fcom.alibaba.dubbo.demo.DemoService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D6676%26qos.port%3D33333%26side%3Dconsumer%26timestamp%3D1525502466637.儲存了服務消費方ip、group、介面名稱、版本、應用名稱等.消費端本地會快取遠端服務提供者(每個提供者對應一個Invoker物件)、註冊中心配置、路由配置資訊。監聽註冊中心路徑是/dubbo/interfaceClass/providers和/dubbo/{interfaceClass}/configurators, /dubbo/${interfaceClass}/routers的節點,當提供者、配置、路由資訊發生變化之後註冊中心會通知消費者重新整理本地快取。Dubbo框架通過在消費端快取提供者的資訊消除對註冊中心的強依賴,即使註冊中心掛了服務依然可用。
4.在RegistryProtocol.doRefer()中註冊完後,呼叫com.alibaba.dubbo.registry.support.FailbackRegistry.subscribe(URL, NotifyListener)進入com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry.doSubscribe(URL, NotifyListener)監聽註冊中心的節點變化,註冊的節點類似服務端,有[dubbo://192.168.11.190:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3456&side=provider&timestamp=1525499817390, empty://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637, empty://192.168.11.190/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=6676&qos.port=33333&side=consumer&timestamp=1525502466637]三個,主要是provider地址,configurators,routers三個.
5.在com.alibaba.dubbo.registry.integration.RegistryDirectory.subscribe(URL)中進入com.alibaba.dubbo.registry.support.FailbackRegistry.notify(),呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.refer()建立DubboInvoker物件.屬性是ExchangeClient,version,invokers,生成的ExchangeClient的數量根據service_share_connect判斷,預設是true,即呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getSharedClient(URL)只生成一個連線.呼叫com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain()生成Filter鏈,預設有三個[ConsumerContextFilter, FutureFilter, MonitorFilter].我理解是為了用於DubboMonitor對每次呼叫進行監控。類似spring中的aop.
6.RegistryProtocol.doRefer方法的最後,呼叫com.alibaba.dubbo.rpc.cluster.Cluster.join()?由Cluster元件來建立一個Invoker並返回,預設是com.alibaba.dubbo.rpc.cluster.support.FailoverCluster.join(Directory<T>),FailoverCluster的directory屬性引用上面建立的RegistryDirectory物件。返回生成的Invoker例項之後,由ProxyFactory生成一個持有該Invoker例項的代理,代理回撥時會啟用該Invoker呼叫它的invoke方法.進入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker()呼叫doInvoke.

四.遠端服務呼叫流程.
消費端呼叫提供端服務的過程要執行下面幾個步驟: 
a. 消費端觸發請求 
b. 消費端請求編碼 
c. 提供端請求解碼 
d. 提供端處理請求 
e. 提供端響應結果編碼 
f. 消費端響應結果解碼
(四.1)消費端觸發請求
1.在消費者初始化的時候,會生成一個消費者代理(jdk的動態代理)註冊到容器中,該代理回撥中持有一個MockClusterInvoker例項,消費呼叫服務介面時它的invoke會被呼叫.呼叫需要的interface後,進入代理物件的對應方法中,然後統一進入com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler.invoke().如果是toString(),hashCode(),equals()方法,直接進入MockClusterInvoker物件的相應方法.此時呼叫com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker.invoke(Invocation),引數為new出一個RpcInvocation物件.RpcInvocation物件的屬性為呼叫的interface的方法名,引數型別,引數,invoker物件.在這個invoke方法中,判斷請求是否需要mock,是否配置了mock屬性,是force mock(消費方的呼叫直接返回null,不發起遠端呼叫,用來遮蔽不重要的應用對服務端的影響)還是fail mock(呼叫失敗返回null,不拋異常,用於服務不穩定時對服務端的影響).預設mock為false,主要用於服務降級,對應dubbo monitor中的動態配置.進入 com.alibaba.dubbo.rpc.cluster.support.FailoverClusterInvoker.doInvoke().常用的cluster叢集容錯架構還有Failfast Cluster(快速失敗,只發起一次呼叫,失敗立即報錯,用於非冪等性操作,比如網路情況不好,寫操作),Failsafe(失敗安全,報異常時直接忽略,用於寫入日誌等),Failback Cluster(失敗自動恢復),
Forking Cluster(並行呼叫多個,一個成功就返回),Broadcst Cluster(廣播呼叫,任何一個報錯則報錯,比如更新快取等). cluster主要將directory中的多個invoker封裝成一個,區分不同的失敗重試策略.預設的FailoverCluster是失敗自動切換,當出現失敗,重試其它伺服器,預設重試3次.
2.FailoverClusterInvoker.doInvoke()中。先呼叫checkInvokers()檢查對應的invokers是否為空,為空則丟擲沒有provider的異常日誌.然後根據retries次數的配置得到dubbo的嘗試呼叫次數,預設是3次。如果是第一次呼叫直接進入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.select()根據負載均衡策略選擇一個invoker呼叫.如果是第二次開始要先進入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.list()(多型實際是FailoverClusterInvoker),進入com.alibaba.dubbo.registry.integration.RegistryDirectory.List(Invocation)(Directory有兩種,StaticDirectory(用於多個註冊中心)和RegistryDirectory,代表一組可以用的invoker,有notify方法,用於註冊中心的回撥,修改methodInvokerMap來儲存動態變化的多個invoker,或者router路由的變化,回撥在com.alibaba.dubbo.registry.integration.RegistryDirectory.notify()中執行)->com.alibaba.dubbo.registry.integration.RegistryDirectory.doList(Invocation)中執行路由規則.通過directory,在methodInvokerMap中找出所有的invoker.然後在com.alibaba.dubbo.rpc.cluster.directory.AbstractDirectory.list(Invocation)中繼續通過router,分為Script和Condition兩種,Condition路由就是管理後臺配置的路由規則,比如對於某個service,當消費端的ip滿足什麼條件呼叫滿足對應條件的服務端.可以啟用或者禁用某條路由規則。.呼叫com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector.route()->com.alibaba.dubbo.rpc.cluster.router.MockInvokersSelector.getNormalInvokers()找出可以執行的invoker。
3.在FailoverClusterInvoker.doInvoke()中,如果是第二次dubbo呼叫讀取到所有符合條件的服務提供者invoker之後,或者第一次dubbo呼叫進入com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker.select().如果invokers多於一個,進入com.alibaba.dubbo.rpc.cluster.LoadBalance.select().由LoadBalance元件執行負載均衡,從中挑選一個invoker進行呼叫,預設是進入com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance.doSelect()隨機演算法.框架內建支援的負載均衡演算法包括random(隨機)、roundrobin(R-R迴圈)、leastactive(最不活躍)、consistenthash(一致性hash),應用可配置.我除錯時只有一臺機器提供了一個invoker,所以沒有進入這段程式碼.
4.在FailoverClusterInvoker.doInvoke()中選擇好invoker之後,進入com.alibaba.dubbo.rpc.Invoker.invoke(Invocation)->ConsumerContextFilter.invoke()->FutureFilter.invoke()->MonitorFilter.invoke()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboInvoker.doInvoke(Invocation).methodInvokerMap儲存的是持有DubboInvoker(dubbo協議)例項的InvokerDelegete物件,是Invoker-Filter鏈的頭部,先啟用Filter連然後最終調到DubboInvoker.doInvoke(RpcInvocation),
5.在DubboInvoker.doInvoke()中先獲取當前呼叫的ExchangeClient,如果有多個Client,隨機選一個.然後判斷呼叫是不是非同步的,單向的,超時時間(預設前兩個都不是,超時1秒).遠端呼叫分三種類型: 
a.單向呼叫,無需獲取關注呼叫結果的,無需等待介面返回結果,注意呼叫結果不要單純跟返回值混淆了,異常也是呼叫結果。 
b.非同步呼叫,需要關注返回結果,但是不會同步等待介面呼叫結束,會非同步的獲取返回返回結果,這種情況給呼叫者返回一個Future,但是不同步等待Future.get返回呼叫結果 
c.同步呼叫,需要同步等待服務呼叫結束獲取呼叫結果,給呼叫者返回一個Future並且Future.get等待結果,此時介面呼叫執行緒會掛起等待響應。
如果使用者配置了多個connections按順序選擇一個ExchangeClient和伺服器通訊,同步呼叫時依次呼叫com.alibaba.dubbo.rpc.protocol.dubbo.ReferenceCountExchangeClient.request()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeClient.request()(加入client的目的我理解是區分不同的netty,p2p,netty4等網路客戶端呼叫,真正的netty等呼叫作為channel處理)->這裡的request引數是RpcInvocation物件,包含呼叫的方法、引數等資訊,timeout引數是介面超時時間,把這些資訊封裝在Request物件中,依次呼叫NettyClient.send()->com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send(),這個channel物件就是和服務端打交道的NettyClient例項,NettyClient.send呼叫com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send()->org.jboss.netty.channel.Channel.write()->com.alibaba.dubbo.remoting.transport.netty.NettyHandler.writeRequested()->org.jboss.netty.handler.codec.oneone.OneToOneEncoder.handleDownstream()傳送訊息,在該方法中,先開始進行編碼。編碼依次進入com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalEncoder.encode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.encode()->com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encodeRequest(),在該方法中具體進行dubbo協議的編碼.在OneToOneEncoder.handleDownstream()中編碼結束後,呼叫com.alibaba.dubbo.remoting.ChannelHandler.sent()傳送訊息
(四.2)消費端請求編碼
1進入ExchangeCodec.encodeRequest().根據協議,訊息中寫入16個位元組的訊息頭: 
a、1-2位元組,固定的魔數 
b、第3個位元組,第8位儲存資料型別是請求資料還是響應資料,其它7位儲存序列化型別,約定和服務端的序列化-反序列化協議 
c、5-12個位元組,請求id 
d、13-16個位元組,請求資料長度.框架中預設的序列化協議是hessian2。訊息體資料包含dubbo版本號、介面名稱、介面版本、方法名稱、引數型別列表、引數、附加資訊,把它們按順序依次序列化,資料寫入到型別為ChannelBuffer的buffer引數中,然後把ChannelBuffer封裝成Netty框架的org.jboss.netty.buffer.ChannelBuffer。如果引數中有回撥介面,還需要在消費端啟動埠監聽提供端的回撥.資料部分的封裝通過com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.encodeRequestData()實現。然後把封裝好的ChannelBuffer寫到鏈路傳送到服務端,這裡消費端前半部分的工作就完成.最終通過org.jboss.netty.handler.codec.oneone.OneToOneEncoder.doEncode中呼叫org.jboss.netty.channel.Channels.write()傳送。
(四.3)提供端請求解碼
1.當有客戶端請求進來時,進入com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalDecoder.messageReceived(),呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboCountCodec.decode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decode()->com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.decodeBody()按dubbo協議解析dubbo協議頭,魔數等.然後呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcInvocation.decode()解析出具體的介面類,呼叫com.alibaba.dubbo.rpc.protocol.dubbo.CallbackServiceCodec.decodeInvocationArgument()解析出引數.
2.在DubboCountCodec.decode()中呼叫DubboCodec.decode()解析出Request物件後,封裝成MultiMessage物件.
3.在InternalDecoder.messageReceived()解析出訊息後,呼叫org.jboss.netty.channel.Channels.fireMessageReceived(ChannelHandlerContext, Object, SocketAddress)通知訊息的接受,啟用下一個處理器的messageReceived事件.
4.事件進入到下一個處理器NettyHandler,進入com.alibaba.dubbo.remoting.transport.netty.NettyHandler.messageReceived()->com.alibaba.dubbo.remoting.transport.MultiMessageHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeartbeatHandler.received()->com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.received()。通過執行緒池啟動新的執行緒處理資料.
5.新的處理請求執行緒進入com.alibaba.dubbo.remoting.transport.dispatcher.ChannelEventRunnable.run()->com.alibaba.dubbo.remoting.transport.DecodeHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.received()->com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleRequest()(建立Response物件),Response物件的body部分呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.requestHandler.new ExchangeHandlerAdapter() {...}.reply(ExchangeChannel, Object)。該方法先獲取Invoker物件,然後呼叫Invoker的invoke方法.
(四.4)提供端處理請求.
1.DubboProtocol中reply方法處理邏輯。呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol.getInvoker()查詢提供端請求對應的Invoker,在介面提供者初始化時,每個介面都會建立一個Invoker和Exporter,Exporter持有invoker例項,Exporter物件儲存在DubboProtocol的exporterMap中。這個map是重點,map的key是由URL生成的serviceKey,格式為com.alibaba.dubbo.demo.DemoService:20880.value是對應的exporter,格式為{com.alibaba.dubbo.demo.DemoService:20880=com.alib
[email protected]
59babb62}.exporter的屬性中有對應的Invoker.此時通過Invocation中的資訊就可還原該serviceKey並且找到對應的Exporter和Invoker,在分析提供者初始化程式碼時知道它是Invoker-Filter的頭節點,啟用Filter後呼叫由ProxyFactory生成的Invoker.進入com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker(...).new AbstractProxyInvoker() {...}.doInvoke()呼叫處理具體的處理方法。
2.在DubboProtocol中的reploy方法中,呼叫com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper.buildInvokerChain(...).new Invoker() {...}.invoke(Invocation)這是一個鏈式的filter.每一個filter好像是一個protocol,具體的filter有EchoFilter,ClassLoaderFilter,ContextFilter,TraceFilter,TimeoutFilter,MonitorFilter,ExceptionFilter,DelegateProviderMetaDataInvoker->invoke->com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory.getInvoker(...).new AbstractProxyInvoker() {...}.doInvoke()->com.alibaba.dubbo.demo.provider.DemoServiceImpl.sayHello(String)真正的實現類.
(四.5)提供端響應結果編碼
在HeaderExchangeHandler.received()方法中呼叫HeaderExchangeHandler.handleRequest()執行完具體的interface實現類後,返回Response物件。呼叫com.alibaba.dubbo.remoting.transport.netty.NettyChannel.send(Object, boolean)。類似步驟(四.1)中的5進行編碼傳送處理.通過Code2元件和請求編碼時使用的元件一樣,把響應型別和響應結果依次寫回到客戶端,根據協議會寫入16個位元組的資料頭,包括: 
a、1-2位元組魔數 
b、第3個位元組,序列化元件型別,約定和客戶端的序列化-反序列化協議 
c、第4個位元組,響應狀態,是OK還是error 
d、5-13個位元組,響應id,這裡的id和request中的id一樣 
e、13-16個位元組,響應資料長度
此步呼叫com.alibaba.dubbo.remoting.exchange.codec.ExchangeCodec.encodeResponse執行。返回結果有三種結果:1、沒有返回值即返回型別是void;2、有返回值並且執行成功;3、服務呼叫異常。此步呼叫com.alibaba.dubbo.rpc.protocol.dubbo.DubboCodec.encodeResponseData執行。解碼後的資料會寫入到通訊鏈路中.
(四.6)消費端響應結果解碼
1.服務端給客戶端回寫資料之後,客戶端會收到IO事件,一個上行事件。NettyClient中有兩個上行事件處理器NettyCodecAdapter.decoder和NettyHandler,按照順序decoder先執行對服務端傳過來的資料進行解碼,解析出序列化協議、響應狀態、響應id(即請求id)。把響應body資料讀到DecodeableRpcResult物件中,進行解析同時載入處理原始Request資料,這個Request物件在請求時會被快取到DefaultFuture中,載入Request的目的是因為Request中Invocation中攜帶了服務介面的返回值型別資訊,需要根據這個型別把響應解析建立對應型別的物件。此步在
com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode中實現。
2.建立Response物件並且把解析出結果或異常設定到Response中。 
decoder把響應解析成Response物件中,NettyHandler接著往下處理,同樣觸發它的messageReceive事件,在提供端解碼的時候看到了,它的handler封裝關係是:DecodeHandler->HeaderExchangeHandler->DubboProtocol.requestHandler,主要處理在HeaderExchangeHandler中:com.alibaba.dubbo.remoting.exchange.support.header.HeaderExchangeHandler.handleResponse實現。
3.進入com.alibaba.dubbo.remoting.exchange.support.DefaultFuture.doReceived.喚醒呼叫者執行緒,並且把Response設定到DefaultFuture中,在消費者觸發請求的程式碼中可以看到,消費端呼叫介面的時候請求寫到提供端之後,會呼叫DefaultFuture.get阻塞等待響應結果.在done這個Condition上進行條件等待,DefaultFuture.doReceive時,設定response喚醒done,此時呼叫執行緒被喚醒並且檢查是否已經有了response(避免假喚醒),喚醒之後返回response中的result,呼叫端即拿到了介面的呼叫結果(返回值或異常),整個遠端服務介面的呼叫流程就完成了.
(四.6)超時處理
在進行介面呼叫時會出現兩種情況:介面呼叫成功、介面呼叫異常,其實還有一種情況就是介面呼叫超時。在消費端等待介面返回時,有個timeout引數,這個時間是使用者設定的,可在消費端設定也可以在提供端設定,done.await等待時,會出現兩種情況跳出while迴圈,一是執行緒被喚醒並且已經有了response,二是等待時間已經超過timeout,此時也會跳出while,當跳出while迴圈並且Future中沒有response時,就說明介面已超時丟擲TimeoutException,框架把TimeoutException封裝成RpcException拋給應用層

參考連結:
生產者提供服務過程
http://www.cnblogs.com/man-li/p/4326580.html 
http://www.cnblogs.com/man-li/p/4323281.html
 http://www.cnblogs.com/man-li/p/4323277.html
http://blog.csdn.net/jycwl/article/details/51243416
http://blog.csdn.net/pentiumchen/article/details/53227640
http://blog.csdn.net/pentiumchen/article/details/53227718
http://blog.csdn.net/pentiumchen/article/details/53227844
消費端提供過程
http://blog.csdn.net/pentiumchen/article/details/53227718
網路呼叫封裝過程
http://blog.csdn.net/pentiumchen/article/details/53227844