關於dubbo的描述就不再贅述,直接進入主題,那就是dubbo的工作原理。dubbo分為服務提供者和服務消費者,主要的工作內容有以下幾點:提供者暴露服務、消費者引入服務、提供者和消費者和注冊中心之間的通信、消費者消費服務、監控中心、其他擴展
一、provider暴露服務
1、首先provider可以在配置文件中配置自己可以提供那些服務,通過<dubbo:service>可以進行配置或者注解的方式,並且在配置的時候需要配置注冊中心、應用名、節點地址、通信協議等一系列參數(以DemoService為例子,下同)
2、dubbo是基於spring的,dubbo提供的服務在spring看來就是一個bean,叫做ServiceBean,而ServiceBean實現了Spring的InitializingBean、ApplicationContextAware、ApplicationListener等接口,所以當spring啟動完成之后,ServiceBean實際上就已經被加載到spring容器中了,(此時DemoService已經作為一個bean存在spring容器中,但是還沒有注冊到注冊中心,也沒有暴露給外部,只是作為一個最基本的bean的存在),所以ServiceBean還監聽了applicationContext啟動完成的事件,執行額外的操作。
3、當spring容器啟動完成之后,ServiceBean就需要將自己暴露給外部並且注冊到注冊中心了,這一步是ServiceBean的export方法中執行,也可以叫做導出方法
4、首先加載provider端dubbo端配置,如應用名稱、注冊中心地址、通信協議、端口號等基本信息,並且對這些配置進行讀取獲取是設置默認值,並且根據不同配置進行不同處理,如是否延遲加載等
5、所有配置信息加載完畢,就將這些配置信息進行組裝到一起,封裝成了一個URL對象,一個URL對象就包含了一個服務所有的信息,包含了接口到方法名稱、參數列表、dubbo的配置信息等
6、現在DemoService被封裝成了一個URL,那么就可以進行暴露出去了,暴露出去的意思實際就是告訴別人需要以什么樣的協議來調用,所以服務的暴露針對不同的協議暴露的方式也不同。
7、Protocol接口提供了暴露服務和引入服務兩個方法,不同的協議實現就需要實現這個接口來進行暴露服務,默認是dubbo協議,所以就通過DubboProtocol實現類來進行服務的暴露
8、暴露的過程實際就是將服務存入一個全局的Map中,key就是以URL為基礎創建的唯一key,value就是這個服務
9、下一步,既然是遠程調用,那么消費者就需要連接提供者,提供者這邊就需要開啟一個端口等待消費者的連接
10、dubbo默認采用的是Netty框架,所以在暴露服務的時候就會根據服務配置的端口號啟動Netty服務器,並且以host+port為key,NettyServer為value存入Map中(這樣做的好處是不同服務可以通過不同的Netty服務器處理)
到這里服務的暴露過程基本上走完,實際上就是將服務封裝成一個對象存入全局Map中,然后啟動一個Netty服務器監聽消費者的消費。
實現細節:
當服務被消費,那就需要被執行,如DemoService的一個方法被消費,那么這個方法最終是需要被執行的。那么如何被執行呢?兩種思路:一種是服務暴露的時候定義一個執行器,可以執行DemoService的實現類的方法;一種是服務被消費的時候再來執行。很顯然第一種方式跟靠譜,因為這樣就可以在服務暴露的時候就提前知道了服務的方法該如何執行,具體執行的時候傳入不同的參數就好了。dubbo也是這么干的,所以這里就涉及到了一個接口叫做Invoker。
Invoker是dubbo很核心的一個概念,首先不關心它是如何實現的,首先得了解它能干嘛。可以理解為Invoker就是一個執行體,一個Invoker對應一個接口,這個接口的方法就可以通過Invoker來執行,如DemoService的所有方法都可以通過這個Invoker來執行。
而Invoker就是在服務暴露的時候創建的,就是步驟5中的創建的URL對象來創建的,而步驟8中暴露服務的時候實際也就是將invoker對象作為參數進行暴露,暴露成功之后會再封裝出一個Exporter對象。因為服務可以被暴露也可以取消暴露,Exporter對象中就包含了Invoker對象以及暴露的狀態。所以第8步中最終存入全局Map的就是這個Exporter對象。
再捋一捋:服務暴露需要根據不同的協議去暴露,所以需要執行不同協議對象procotol實現類,每個procotol中有一個Map,key為服務的唯一標識,value為Exporter對象;Exporter對象可以調用getInvoker()得到這個服務的Invoker對象,得到了這個Invoker對象就可以執行具體服務的方法了。至於Invoker具體怎么執行方法下文和消費者的消費過程一同分析
二、consumer引入服務
1、消費者的啟動過程和提供者大致差不多,只不過消費者的bean叫做ReferenceBean,也是在spring啟動的時候進行加載初始化
2、當需要消費服務時,首先的從容器中獲取bean也就是執行getObject()得到,此時就會在getObject方法中執行init()方法去引入服務
3、執行init方法時首先也是進行dubbo配置的讀取和加載等,並且將一切配置信息整合到一個map中(提供者也是先放入map然后封裝成URL對象)
4、然后根據map中的配置信息,執行createProxy方法來返回一個服務的實例,如DemoService的實現類實例 demoService,那么這個demoService就可以直接執行DemoService接口中的方法了。
5、前四步好理解,就是根據配置參數獲取了接口的實現類對象,然后就可以去執行方法了。那么現在的重點就是這個實現類是如何生成的,也就是createProxy方法是如何實現的
6、回顧提供者暴露服務的過程可以知道一個invoker是一個執行體,暴露服務的時候通過Procotol接口的方法將Invoker暴露出去,那么消費者可以根據Procotol接口獲取Invoker對象么?答案是肯定的。
7、消費者根據配置信息的map也創建了URL對象,然后通過Procotol的refer方法可以獲取到一個Invoker對象,這個Invoker對象就是可以執行服務的方法的執行體
8、invoker對象的創建過程會和服務提供者進行連接,以netty為例子就是創建了Netty的客戶端和提供者那邊的Netty服務端進行連接,然后得到的連接對象和服務信息共同構造出了invoker對象
9、而消費者不能直接使用Invoker,因為不能使用DemoService service = invoker, 所以需要將invoker轉化成接口的實現類對象。
10、這里就采用了代理模式,通過字節碼生成技術,根據invoker對象動態生成了一個服務的實現類,那么這個實現類執行具體方法的時候實際就是通過invoker來執行了。
總結:服務引入的過程實際就是作為一個客戶端,創建了和服務器的一個連接,得到了一個invoker對象,並通過invoker對象動態代理的方式得到服務的實現類,實現類的方法執行實際就是通過invoker來執行的。
三、consumer消費服務的執行過程
上面提供者和消費者兩端都提到Invoker對象,所以實際的操作都是通過Invoker來執行的。
顯然消費端的Invoker實際工作內容實際就是將執行的方法轉化成字節流的形式通過網絡通信發送給服務端,當然首先需要進行序列化,默認采用netty通信
服務端接收到字節流信息首先是進行解碼,然后到的請求的具體參數,根據請求內容就可以得到是哪一個Exporter對象,然后就知道是哪一個Invoker對象,那么就可以通過執行這個invoker的方法進行方法的具體執行。
總結:
1、消費端執行DemoService service的具體方法,實際是執行代理實現類的對應方法,代理類的方法執行實際是通過invoker來執行,invoker執行的時候實際就是通過網絡通信將請求發送給服務端
2、服務端接收消息進行解碼,得到請求的信息,根據請求信息得到Exporter對象,根據Exporter對象可以獲取Invoker對象,然后通過Invoker來執行具體的方法(這個Invoker和消費端的Invoker不是同一個實現)