RPC和Dubbo簡介

RPC協(xié)議

RPC:遠(yuǎn)程過程調(diào)用,原則上來說系統(tǒng)間跨進(jìn)程的調(diào)用都屬于RPC范疇

RMI/HTTP/dubbo/Spring Cloud/thrift

RPC框架如何實(shí)現(xiàn)分布式環(huán)境下的遠(yuǎn)程調(diào)用

在一個(gè)典型的RPC的使用場景中,包含了服務(wù)發(fā)現(xiàn),負(fù)載,容錯(cuò),網(wǎng)絡(luò)傳輸,序列化等組件,其中RPC協(xié)議指明了程序如何進(jìn)行網(wǎng)絡(luò)傳輸和序列化。


RPC協(xié)議的組成

RPC協(xié)議的組成

1. 地址:服務(wù)提供者地址

2.端口:協(xié)議指定開放的端口

3.報(bào)文編碼:協(xié)議報(bào)文編碼,分為請求頭和請求體兩部分

4.序列化方式:將請求體序列化成對象,具體的方式有Hessian2Serialization,DubboSerialization,JavaSerialization,JsonSerization等

5.運(yùn)行服務(wù):網(wǎng)絡(luò)傳輸實(shí)現(xiàn),實(shí)現(xiàn)方式主要有netty,mina,RMI服務(wù),Servlet容器(jetty,tomcat,jboss)

RPC協(xié)議報(bào)文編碼與實(shí)現(xiàn)

RPC傳輸實(shí)現(xiàn)

基于TCP/IP作為基礎(chǔ)使用Socket或Netty或者M(jìn)ina等網(wǎng)絡(luò)編程組件實(shí)現(xiàn)。TCP是面向字節(jié)流的無邊界協(xié)議,其只管負(fù)責(zé)數(shù)據(jù)傳輸并不會區(qū)分每次請求鎖對應(yīng)的消息,這樣就會出現(xiàn)TCP協(xié)議中的拆包和粘包的問題。

粘包的原因:TCP協(xié)議中發(fā)送端和接收端都會有緩沖區(qū)

粘包的解決辦法:(1)消息固定長度(2)基于特殊字符進(jìn)行標(biāo)識分割(3)使用帶消息頭的協(xié)議,消息頭中標(biāo)識消息體的大小,然后根據(jù)長度去讀取消息的內(nèi)容,類似于HTTP協(xié)議中的content-length

Dubbo支持的協(xié)議

Dubbo框架支持dubbo、rmi、hessian、http、webservice、thrift、redis等多種協(xié)議,但是Dubbo官網(wǎng)是推薦我們使用Dubbo協(xié)議的,采用單一長連接和 NIO 異步通訊,適合于小數(shù)據(jù)量大并發(fā)的服務(wù)調(diào)用,以及服務(wù)消費(fèi)者機(jī)器數(shù)遠(yuǎn)大于服務(wù)提供者機(jī)器數(shù)的情況。Hessian底層采用HTTP通訊(同步),采用Servlet暴露服務(wù)。適用于傳入傳出參數(shù)數(shù)據(jù)包較大,提供者比消費(fèi)者個(gè)數(shù)多,提供者壓力較大,可傳文件。下面重點(diǎn)介紹Dubbo協(xié)議:

Dubbo協(xié)議頭格式

如上圖可知header總包含了16個(gè)字節(jié)的數(shù)據(jù),其中前兩個(gè)字節(jié)為魔數(shù),類似Class類文件里面的作用,這里用來標(biāo)識一個(gè)幀的開始,固定為0xdabb其中第一個(gè)字節(jié)固定為0xda,第二個(gè)字節(jié)固定為0xbb.?

后面緊這的一個(gè)字節(jié)是請求和序列化標(biāo)記的組合結(jié)果requstflag|serializationId。其中高四位表示請求的requstflag,低四位是序列化的方式:

DubboSerialization:0001

FastJsonSerialization:0110

Hessian2Serialization:0010

JavaSerialization:0011

response status:是響應(yīng)報(bào)文里面才設(shè)置(請求報(bào)文里面不設(shè)置),用來標(biāo)識響應(yīng)的結(jié)果碼

request id:消息請求ID,long類型,每一個(gè)請求的唯一識別id(采用異步通訊的方式,用來把請求request和返回的response對應(yīng)上)

body length:消息體body長度,int類型

Dubbo簡介

Dubbo提供了三個(gè)關(guān)鍵功能:基于接口的遠(yuǎn)程調(diào)用,容錯(cuò)與負(fù)載均衡,服務(wù)自動注冊與發(fā)現(xiàn)。

Dubbo的結(jié)構(gòu)圖

節(jié)點(diǎn)角色說明

? ? ? Provider: 暴露服務(wù)的服務(wù)提供方。

? ? ? Consumer: 調(diào)用遠(yuǎn)程服務(wù)的服務(wù)消費(fèi)方。

? ? ? Registry: 服務(wù)注冊與發(fā)現(xiàn)的注冊中心。

? ? ? Monitor: 統(tǒng)計(jì)服務(wù)的調(diào)用次調(diào)和調(diào)用時(shí)間的監(jiān)控中心。

? ? ? Container: 服務(wù)運(yùn)行容器。

這點(diǎn)我覺得非常好,角色分明,可以根據(jù)每個(gè)節(jié)點(diǎn)角色的狀態(tài)來確定該服務(wù)是否正常。

調(diào)用關(guān)系說明

0. 服務(wù)容器負(fù)責(zé)啟動,加載,運(yùn)行服務(wù)提供者。

1. 服務(wù)提供者在啟動時(shí),向注冊中心注冊自己提供的服務(wù)。

2. 服務(wù)消費(fèi)者在啟動時(shí),向注冊中心訂閱自己所需的服務(wù)。

3. 注冊中心返回服務(wù)提供者地址列表給消費(fèi)者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費(fèi)者。

4. 服務(wù)消費(fèi)者,從提供者地址列表中,基于軟負(fù)載均衡算法,選一臺提供者進(jìn)行調(diào)用,如果調(diào)用失敗,再選另一臺調(diào)用。

5. 服務(wù)消費(fèi)者和提供者,在內(nèi)存中累計(jì)調(diào)用次數(shù)和調(diào)用時(shí)間,定時(shí)每分鐘發(fā)送一次統(tǒng)計(jì)數(shù)據(jù)到監(jiān)控中心。

連通性

1. 注冊中心負(fù)責(zé)服務(wù)地址的注冊與查找,相當(dāng)于目錄服務(wù),服務(wù)提供者和消費(fèi)者只在啟動時(shí)與注冊中心交互,注冊中心不轉(zhuǎn)發(fā)請求,壓力較小

2. 監(jiān)控中心負(fù)責(zé)統(tǒng)計(jì)各服務(wù)調(diào)用次數(shù),調(diào)用時(shí)間等,統(tǒng)計(jì)先在內(nèi)存匯總后每分鐘一次發(fā)送到監(jiān)控中心服務(wù)器,并以報(bào)表展示

3. 服務(wù)提供者向注冊中心注冊其提供的服務(wù),并匯報(bào)調(diào)用時(shí)間到監(jiān)控中心,此時(shí)間不包含網(wǎng)絡(luò)開銷

4. 服務(wù)消費(fèi)者向注冊中心獲取服務(wù)提供者地址列表,并根據(jù)負(fù)載算法直接調(diào)用提供者,同時(shí)匯報(bào)調(diào)用時(shí)間到監(jiān)控中心,此時(shí)間包含網(wǎng)絡(luò)開銷

5. 注冊中心,服務(wù)提供者,服務(wù)消費(fèi)者三者之間均為長連接,監(jiān)控中心除外

6. 注冊中心通過長連接感知服務(wù)提供者的存在,服務(wù)提供者宕機(jī),注冊中心將立即推送事件通知消費(fèi)者

7. 注冊中心和監(jiān)控中心全部宕機(jī),不影響已運(yùn)行的提供者和消費(fèi)者,消費(fèi)者在本地緩存了提供者列表

8. 注冊中心和監(jiān)控中心都是可選的,服務(wù)消費(fèi)者可以直連服務(wù)提供者

健壯性

1. 監(jiān)控中心宕掉不影響使用,只是丟失部分采樣數(shù)據(jù)

2. 數(shù)據(jù)庫宕掉后,注冊中心仍能通過緩存提供服務(wù)列表查詢,但不能注冊新服務(wù)

3. 注冊中心對等集群,任意一臺宕掉后,將自動切換到另一臺

4. 注冊中心全部宕掉后,服務(wù)提供者和服務(wù)消費(fèi)者仍能通過本地緩存通訊

5. 服務(wù)提供者無狀態(tài),任意一臺宕掉后,不影響使用

6. 服務(wù)提供者全部宕掉后,服務(wù)消費(fèi)者應(yīng)用將無法使用,并無限次重連等待服務(wù)提供者恢復(fù)

Dubbo模型


Transporter:mina, netty, grizzy

Serialization:dubbo, hessian2, java, json

Dispatcher:all, direct, message, execution, connection

ThreadPool:fixed, cached

特點(diǎn):Dubbo缺省協(xié)議采用單一長連接和NIO異步通訊,適合于小數(shù)據(jù)量大并發(fā)的服務(wù)調(diào)用,以及服務(wù)消費(fèi)者機(jī)器數(shù)遠(yuǎn)大于服務(wù)提供者機(jī)器數(shù)的情況。

缺點(diǎn):Dubbo缺省協(xié)議不適合傳送大數(shù)據(jù)量的服務(wù),比如傳文件,傳視頻等,除非請求量很低。

Dubbo協(xié)議

缺省協(xié)議,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。

連接個(gè)數(shù):單連接

連接方式:長連接

傳輸協(xié)議:TCP

傳輸方式:NIO異步傳輸

序列化:Hessian二進(jìn)制序列化

適用范圍:傳入傳出參數(shù)數(shù)據(jù)包較?。ńㄗh小于100K),消費(fèi)者比提供者個(gè)數(shù)多,單一消費(fèi)者無法壓滿提供者,盡量不要用dubbo協(xié)議傳輸大文件或超大字符串。

適用場景:常規(guī)遠(yuǎn)程服務(wù)方法調(diào)用

線程模型

首先明確一個(gè)基本概念:IO線程和業(yè)務(wù)線程的區(qū)別

IO線程:配置在netty連接點(diǎn)的用于處理網(wǎng)絡(luò)數(shù)據(jù)的線程,主要處理編解碼等直接與網(wǎng)絡(luò)數(shù)據(jù)打交道的事件。Dubbo默認(rèn)的底層網(wǎng)絡(luò)通訊使用的是Netty,服務(wù)提供方NettyServer使用兩級線程池,其中EventLoopGroup(boss)主要用來接受客戶端的鏈接請求,并把接受的請求分發(fā)給EventLoopGroup(worker) 來處理,boss和worker線程組我們稱之為IO線程。

業(yè)務(wù)線程:用于處理具體業(yè)務(wù)邏輯的線程,可以理解為自己在provider上寫的代碼所執(zhí)行的線程環(huán)境。

事件處理線程說明

如果事件處理的邏輯能迅速完成,并且不會發(fā)起新的IO請求,比如只是在內(nèi)存中記個(gè)標(biāo)識,則直接在IO線程上處理更快,因?yàn)闇p少了線程池調(diào)度。

但如果事件處理邏輯較慢,或者需要發(fā)起新的IO請求,比如需要查詢數(shù)據(jù)庫,則必須派發(fā)到線程池,否則IO線程阻塞,將導(dǎo)致不能接收其它請求。

如果用IO線程處理事件,又在事件處理過程中發(fā)起新的IO請求,比如在連接事件中發(fā)起登錄請求,會報(bào)“可能引發(fā)死鎖”異常,但不會真死鎖。

Dispatcher

all 所有消息都派發(fā)到線程池,包括請求/響應(yīng)/連接事件/斷開事件/心跳等。

direct 所有消息都不派發(fā)到線程池,全部在IO線程上直接執(zhí)行。

message 只有請求響應(yīng)消息派發(fā)到線程池,其它連接斷開事件,心跳等消息,直接在IO線程上執(zhí)行。

execution 只將請求消息派發(fā)到線程池,響應(yīng)消息和其它連接斷開事件,心跳等消息,直接在IO線程上執(zhí)行。

connection 在IO線程上,將連接斷開事件放入隊(duì)列,有序逐個(gè)執(zhí)行,其它消息派發(fā)到線程池。

ThreadPool

fixed 固定大小線程池,啟動時(shí)建立線程,不關(guān)閉,一直持有。(缺省)

cached 緩存線程池,空閑一分鐘自動刪除,需要時(shí)重建。

limited 可伸縮線程池,但池中的線程數(shù)只會增長不會收縮。(為避免收縮時(shí)突然來了大流量引起的性能問題)。配置如下:

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />

線程模型其他相關(guān)參數(shù)

iothreads:指定IO線程池(worker)的線程數(shù)量,默認(rèn)情況下為CPU個(gè)數(shù)+1,因?yàn)檫@個(gè)線程的工作內(nèi)容比較簡單,所以一般情況下我們不會去配置這個(gè)值,除非IO線程的響應(yīng)速度明顯拖慢了整個(gè)工程的響應(yīng),IO線程的默認(rèn)類型是CacheThreadPool,一分鐘的線程死亡時(shí)間。

threadpool:業(yè)務(wù)線程的具體線程類型,默認(rèn)采用的fixed線程池,即線程數(shù)量一定的線程池,這種線程池的好處就是不會頻繁創(chuàng)建線程線程,適合線業(yè)務(wù)比較密集的應(yīng)用。因?yàn)檫@個(gè)數(shù)據(jù)只管關(guān)系到服務(wù)的并發(fā)情況,所以在需要的時(shí)候可以適當(dāng)調(diào)整該數(shù)量來增加工程的并發(fā)。

threads:該參數(shù)就是業(yè)務(wù)線程池的核心線程數(shù)配置,默認(rèn)情況下為200。如果空間有條件的話可以適當(dāng)?shù)靥嵘摂?shù)量,例如提升至400或者500都是可以的。

queues:該數(shù)量指定來在初始化業(yè)務(wù)線程池時(shí)候是否需要排隊(duì)隊(duì)列,如果不設(shè)置的話,業(yè)務(wù)線程池的排隊(duì)隊(duì)列是SynchronousQueue,即不允許業(yè)務(wù)事件排隊(duì),如果線程池沒有空閑線程之后會直接排除異常信息。但是如果配置來queues之后則會使用LinkedBlockingQueue作為排隊(duì)隊(duì)列,queues則代表隊(duì)列的初始隊(duì)列。因?yàn)閝ueues的配置直接關(guān)系到排隊(duì),所以在一般情況下建議不要配置,因?yàn)榫€程池滿的情況下一般期望是直接失敗,然后調(diào)用其他的機(jī)器,而不是再次隊(duì)列繼續(xù)等待,繼續(xù)等待不僅可能會拉低響應(yīng)時(shí)間,而且很有可能會超時(shí)。

acceptes:我們知道threadpool,threads和queues都是控制業(yè)務(wù)線程池的字段,而acceptes就是控制IO線程池的字段。這個(gè)字段標(biāo)示著服務(wù)端可接受的最大長連接數(shù),默認(rèn)情況下為不限制,但是有時(shí)候?yàn)閬肀Wo(hù)服務(wù)器防止連接數(shù)過多導(dǎo)致請求失敗率過高,則可以考慮設(shè)置該字段為一個(gè)定值。

connections:既然服務(wù)端可以設(shè)置最大接收的連接數(shù),那么客戶端也可以設(shè)置與服務(wù)端建立的連接數(shù)。connections可以配置在reference上表示要同對應(yīng)的服務(wù)器建立的長鏈接數(shù)量,默認(rèn)為只建立一條鏈接,如果配置來connections的話則會建立N條長鏈接以提供消費(fèi)者的吞吐量。但是有一點(diǎn)需要注意是如果conenctions的數(shù)量配置大于服務(wù)端的accepts的話,超出的部分會直接報(bào)錯(cuò),表示不支持更多的鏈接,該值不宜配置過多,因?yàn)槿绻鄠€(gè)消費(fèi)者都配置來該值的話很容易到值服務(wù)端的accepts超過預(yù)期數(shù)量而報(bào)錯(cuò)。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容