1. 程式人生 > >深入淺出微服務框架dubbo(四):設計篇

深入淺出微服務框架dubbo(四):設計篇

四、 設計篇

本篇是《深入淺出微服務框架dubbo》的終篇

4.1執行緒模型

netty+zookeeper+curator+dubboProtocol+hession2seralization組合
圖片描述
圖片描述
圖片描述
圖片描述

4.2協議資料格式

這裡引用官網的一張圖:
圖片描述
第三行代表了協議頭,Magic,serializationId,event(是否是事件資料),twoWay(twoWay代表需要響應,oneWay代表不需要響應),req/res(請求還是響應),status(響應狀態,對於req為空)
Id(requestId,響應時帶上,返回時通過id找到future物件喚醒執行緒,通過future找到invocation物件得到響應型別比如(Map,String)幫助完成序列化),dataLength(代表實體資料長度,用於判斷資料是否接收完整,接收完整才能開始decode)

Dubbo協議請求資料實體舉例:
RpcInvocation [methodName=sayHello,
parameterTypes=null,
arguments=null,
attachments={dubbo=2.0.0, path=com.alibaba.dubbo.demo.DemoService, version=0.0.0}]

methodName用於尋找provider(同一服務不一定暴露同樣的方法)
parameterTypes和arguments就不用說了,和methodName一起,傳送到provider可以找到方法名,attachments的path用於在provider尋找到exporter,繼而找到invoker

dubbo協議響應資料實體舉例:
RpcResult [result=Hello world1, response form provider: 172.16.221.1:20880, exception=null]
result是響應結果;exception是異常資訊,最終傳播到使用者呼叫程式碼

4.3通訊

通訊方式由阻塞式IO過度到非阻塞式IO,設計思想也從同步等待過度到事件通知機制,對於client-server模式來講,有以下問題需要處理。
1、連線池:BIO模式下,比如資料庫連線池、httpclient連線池、jedis連線池,連線個數有限,各個執行緒之間需要競爭連線(雖然現在業界有無鎖化jdbc連線池HikariCP,但也避免不了執行緒競爭)。
在NIO模式下,以netty為例,使用者執行緒可以同時向一個channel寫資料(實際是寫入佇列),由iothread負責將佇列裡面的資料傳送出去,資料傳送統一IOthread管理,避免了執行緒對連線的競爭。一般netty模式下,client建立一個連線陣列,每次輪訓取出去傳送。
2、超時處理:BIO在socket設定引數connectionTimeout、socketTimeout可以分別設定連線超時和讀超時,但是在NIO下沒有這種引數設定,就需要自己來判斷,比如連線超時可以通過netty框架中建立連線得到返回的future物件,通過get(timetou)來處理超時;讀超時就需要構造一個future物件,接收到資料後喚醒呼叫get方法的執行緒,get(timeout)超時則丟擲超時異常;如果是非同步處理,就需要將請求事件(可以封裝到future中)快取起來,定期掃描,對超時請求進行處理.
3、長連線保持:對於一個tcp連線,如果不關閉的話,連線會一直保持。但是server端為了避免建立大量連線,需要將空閒連線剔除,通過發心跳可以檢測client連線是否正常,保持長連線,將長時間收不到沒有響應的client連線剔除。
4、連線狀態檢測:有的時候無法收到來自遠端的FIN訊號,比如網路不可用情況下,這個時候需要主動傳送資料來驗證連線是否正常,這個時候就需要client端發起心跳檢測。也可以通過每次在傳送請求之前傳送一個簡單命令來檢測,比如dbcp連線可以傳送”select 1”,jedis連線傳送PING。
5、重連機制:連線斷開需要重新建立連線,對於client連線,連線斷開有可能是由於server端發起主動關閉,還有可能網路故障client在傳送資料時發生IO異常主動關閉,這個時候就需要重建連線保證資料正常傳送和接收。
6、等待響應:同步等待or非同步callback處理,既然NIO是非阻塞式的,那麼為了最大化利用這一特性帶來的好處,非同步callback處理是首選,但是由於業務需要,在傳送完資料需要在當前執行緒下等待結果的返回,由於大多數NIO通訊框架IO執行緒和使用者執行緒是分開的,為了達到同步得到響應結果就需要引入Feture,當前執行緒呼叫get方法(實際上是進入await),IO執行緒收到資料喚醒使用者執行緒,進而去獲取資料。為了達到高效能,建議傳送多次請求最後再一起get等待結果,或者使用非同步+回撥方式。

dubbo在通訊模組上採用長連線,做了很多處理,包括server端、client端雙端的心跳傳送響應,server端對空閒連線檢測和剔除,client端對空閒連線銷燬重建(每次nettyClient對應的channel引用會發生變化,這也是為了dubbo程式碼裡面出現很多getAndAddNettyChannel程式碼的原因),client對超時請求的掃描,client端做了對關閉連線進行銷燬和重連,可以說是非常全面,以達到高效能通訊的目的。
但是我認為在在節省頻寬和減少不必要的壓力時可以採用短+長連線結合,設定空閒連線時間在5分鐘,而不需要做心跳檢測,也不需要非同步重建連線,server端只需要關閉超過5分鐘未傳送資料的連線,client端在收到FIN時自動關閉並將連線狀態設為false,下次傳送資料時重新建立連線。這樣做的好處是減少server端的壓力並降低連線數量,壞處就是如果5分鐘未傳送資料需要同步建立連線

4.4事件處理

IO執行緒是有限的,並且一個執行緒上關聯許多channel,這些channel的事件處理如果交給iothread來做,比如收到查詢請求,然後去資料庫做查詢操作,將導致其它channel上的事件長時間得不到處理或者一個channel上其它事件長時間得不到處理。因此引入執行緒池單獨對事件做處理,比如netty提供了orderedMemoryAwareThreadPoolExecutor。Dubbo也引入了執行緒池,並支援decode可以線上程池中進行處理。

4.5註冊

Dubbo在註冊中心連線恢復做了以下重試,節點建立重試、watch設定重試、通知重試,以zookeeper為例,在連線重連session未失效的情況下,watch以及節點都不會失效,但是在session失效的情況下,watch和節點都會消失,這就是需要有重試機制在保證session失效的時候保證節點依然存在,watch依然監聽失效前所監聽的節點。Dubbo的註冊模組程式碼非常龐大和複雜,如果不考慮那麼多的擴充套件,僅僅使用zookeeper當作註冊中心,並且用curator,程式碼量將大大減少,比如通過curator提供的nodeCache,pathChildrenCache可以很方便的編寫監聽程式碼,程式碼無須重新註冊watch(甚至session失效時),只需要在收到重連事件保證節點還在即可。