如何爲分佈式系統優雅的更換RPC

爲啥需要更換RPC?

很多小夥伴都遇到過需要爲分佈式系統調用更換RPC的問題,爲什麼會遇到這種事呢?其實,在系統搭建初期,需求簡單,架構簡單,最重要的是請求量也少,所以很多系統都採用快速原型開發模式,對rpc的要求不高,隨便找一個順手的或者熟悉的rpc框架套進系統中即可。但是隨着業務複雜度增高,系統承載的請求量增高,可能一開始所採用的RPC框架顯現出一些致命的問題,比如大扇出問題。我們以Thrift爲例。例如隨着業務複雜度的增長,我們面臨着如下的需求。

如圖所示,每一次請求,上游服務都要獲取下游A~Z一共26個服務的結果,然後把這26個服務的結果拼裝返回給前端服務。有人說,26個服務是不是有些誇張了,我的系統中根本沒有遇到過這個情況。這實際一點不誇張,一個業務複雜的系統經過服務拆分,最後拆成一些高內聚低耦合的獨立服務,非常容易達到這樣一個服務種類數,而且26還遠遠不是很多。那麼遇到這種問題,傳統的同步的RPC怎麼解決這個問題呢?

以Thrift爲例,如果需要訪問26個服務,爲了保證請求處理速度,必須要並行訪問各個下游服務(不能串行請求,因爲這將導致 一次請求的響應時間至少爲timeA + timeB + ...... + timeZ),那麼我們只能通過多線程進行併發。

通過多線程併發請求,我們基本能夠達到處理一次請求至多需要 max(timeA, timeB, ......, timeZ),但是實際上要比這個稍多。看樣子我們必須弄一個請求線程池,可是這個池子要多大呢?假如現在前端請求速率爲 P,那麼爲了保證每個請求處理時間都儘可能快,我們需要一個大小爲 26 * P的線程池。雖然,初看起來可能還可以應付,畢竟請求線程在發送網絡請求後,會阻塞在IO,它會放棄CPU,從而使得計算線程獲得CPU,不會浪費多少CPU的資源,但是當P太大就不好了。比如P爲100或者1000,這個時候線程數過多可能就會造成CPU調度開銷增大,因爲它會增加CPU的線程切換負擔。

所以,我們更換RPC,當且僅當,當前的RPC已經造成了系統負擔,對於業務量不大的系統,RPC的更換並沒有必要,但是爲了技術提升你也可以更換RPC,只不過收益可能不大。

需要什麼樣的RPC?

考慮到Thrift對於大扇出並不合適,我們可能需要下面這樣工作模式的RPC。

這種反應器模型(只是簡單舉例子)可以減少請求線程數。這種RPC使用系統的Epoll進行後端服務的請求以及數據的接收,這樣無論多少請求,只使用一個線程完成,通過Epoll的機制在數據到來或者可發送的情況下通知用戶進程,只不過最後需要把接收到的數據返回給計算線程使用。這種模型其實要比Thrift那種那好一些。我自己也在業餘時間實現了一個簡單的RPC框架:http://www.cnblogs.com/haolujun/p/7527313.html ,比較粗糙但是足夠小。
還有有很多開源的RPC框架,fbthrift,GRPC都可以應對大扇出,找到適合你的系統,並且改動量和後期維護成本最低的那個。

如何遷移到新的RPC?

把系統遷移到新的RPC上,除了改動代碼外,就是要做到兼容,系統在遷移過程中可能需要在兩套RPC框架上運行,並且必須做到平滑遷移。例如,一般的分佈式系統可能會長成如下的樣子。

服務B1~B4把自己的地址寫入到ETCD中,但是由於我們一開始並未考慮到RPC的遷移,所以value對應的是服務的地址,沒有服務使用的rpc類型等等。

方案1 添加新key

對於A1~A2,B1~B4,可以先選擇一部分進行平滑過渡,例如我們選擇A1,B1~B2進行遷移。

上線步驟如下:

  • 下線A1,B1,B2。

  • 更新A1配置,使其從新的key:service_new_rpc中讀取後端服務列表。

  • 更新B1,B2配置,使其在新的key:service_new_rpc中註冊自己。

  • 啓動B1,B2。

  • 啓動A1。

  • 對於A2,B3,B4重複如上步驟。

通過這種方式,我們可以平滑的進行服務遷移。但是它的缺點很明顯,需要一個新的key,而且後期還需要一點點把服務挪回到舊的key上。

方案2 代碼兼容

這個方案必須更改一些解析代碼,使其能夠兼容新的ETCD中value的格式,如下圖。

  • 首先改造A代碼,使其能夠兼容新地址解析格式。新地址格式在每個地址後加上RPC類型標識:T(Thrift),G(GRPC),新格式和舊格式的兼容很容易,只需在解析的時候找一下分割符,並判斷分隔符最後一部分是T是G還是什麼都沒有,沒有就默認爲T。

  • 改造A代碼,使其能夠根據後端服務在ETCD中的RPC類型使用不同的RPC框架調用後端。

  • 改造B1~B4的配置,在ETCD中註冊自己的時候把RPC類型順便加上。

  • 改造B1~B2,使用新RPC作爲服務端,並且在註冊的時候把RPC類型設置爲G。

  • 改造B3~B4,使用新RPC作爲服務端,並且在註冊的時候把RPC類型設置爲G。

通過這個步驟,我們就能做到RPC的平滑遷移。這個方式的缺點也有:需要同時維護兩套RPC框架,直到其中一種RPC徹底下線。但是優點也有,沒有增加新key。

總結

更換RPC並不像想象中的那樣困難,只要理清前後邏輯,一點點的遷移,最終你的服務會全部搞定。最重要的問題是你的系統真的達到了非得換RPC的地步了麼?


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章