1. 程式人生 > >分散式服務框架學習筆記2 常用的分散式服務框架 與 通訊框架選擇

分散式服務框架學習筆記2 常用的分散式服務框架 與 通訊框架選擇

傳統垂直架構改造的核心就是要對應用進行服務化,服務化改造使用到的核心技術就是分散式服務框架。

分散式服務框架演進

應用從集中式走向分散式

大規模系統架構的設計一般原則就是儘可能地拆分,以達到更好的獨立擴充套件與伸縮、更靈活的部署、更好的隔離和容錯、更高的開發效率。
具體的拆分策略大體上可以分為橫向拆分和縱向拆分。

縱向拆分

通過對業務進行梳理,根據業務的特性把應用拆開,不同的業務模組獨立部署。

橫向拆分

將核心的、公共的業務拆分出來,通過分散式服務框架對業務進行服務化,消費者通過標準的契約來消費這些服務。
服務提供者獨立打包、部署和演進,與消費者解耦。

服務治理

主要訴求:
1. 生命週期管理
2. 服務容量規劃
3. 執行期治理
4. 服務安全
典型的SOA服務治理生命週期如下圖所示:
- 計劃:確定服務治理的重點
- 定義:定義服務治理模型
- 啟用:實現並實施服務治理
- 度量:根據實施效果,改進服務治理模型

業界分散式服務框架介紹

目前開源的有:
- 阿里的Dubbo
- 噹噹網基於Dubbo增強而來的DubboX
非開源的分散式服務框架,在移動網際網路領域主要有:
- 淘寶的HSF
- 亞馬遜內部使用的分散式服務框架Coral Service
電信行業主要有
- 華為軟體開發的分散式服務框架DSF

分散式服務框架設計

架構原理

通常,分散式服務框架的框架可以抽象為三層
1. RPC層
2. Filter Chain層:服務呼叫職責鏈,提供多種服務呼叫切面供框架自身和使用者擴充套件,例如負載均衡、服務呼叫效能統計、服務呼叫完成通知機制、失敗重發等
3. Service層:主要包括Java動態代理,消費得使用,主要用於將服務提供者的介面封裝成遠端服務呼叫;Java反射,服務提供者使用,根據消費者請求訊息中的介面名、方法名、引數列表反射呼叫服務提供者的介面本地實現類。再向上就是業務的服務介面定義和實現類,對於使用Spring配置化開發的就是Spring Bean,服務由業務來實現,平臺負責將業務介面釋出成遠端服務。

從功能角度看,分散式服務框架通常支包含另外兩個重要功能:服務治理中心和服務註冊中心,業務需求不同,具體實現細節也存在很大差異。以服務註冊中心為例,HSF 使用的是基於資料庫的ConfigServer,Dubbo預設使用的是ZooKeeper。

服務註冊中心負責服務的釋出和通知,通常支援對等叢集部署,某一個服務註冊中心宕機並不會導致整個服務註冊中心叢集不可用。即便整個服務註冊中心全部宕機,也隻影響新服務的註冊和釋出,不影響已經發布的服務的訪問。

服務治理中心通常包含服務治理介面和服務治理Portal,架構師、測試人員和系統運維人員通過服務治理Portal對服務的執行狀態、歷史資料、健康度和呼叫關係等進行中視化的分析和維護,目標就是要持續優化服務,防止服務架構腐化,保證服務高質量執行。

功能特性

分散式服務框架的功能特性

  • 服務訂閱釋出
  • 服務路由
  • 叢集容錯
  • 服務呼叫
  • 多協議
  • 序列化方式
  • 統一配置

效能特性

  • 高效能
  • 低時延
  • 效能線性增長

可靠性

服務治理

  • 服務執行態管控
  • 服務監控
  • 服務生命週期管理
  • 故障快速定界定位
  • 服務安全

通訊框架

  • RPC一般使用長連線
  • 一般使用NIO,採用多路複用技術,一個多路複用器Selector可以同時輪詢多個Channel。由於JDK使用了epoll()代替傳統的select實現,所以它並沒有最大連線控制代碼1024/2048的限制。這也就意味著只需要一個執行緒負責Selector的輪詢,就可以接入成千上萬的客戶端。
  • 常用的NIO框架:Netty

功能設計

分散式服務框架的底層通訊框架首先是一個通用的通訊框架,它不應該與具體的協議繫結。基於通訊框架之上,可以構建私有協議棧和公有協議棧。

服務端設計

  1. 提供上層API(遮蔽底層NIO框架),用於初始化服務端例項,設定服務端通訊相關引數,包括服務端的I/O執行緒池(執行緒組引數)、監聽地址、TCP相關引數、接收和傳送緩衝區大小等;
  2. 提供可擴充套件的編解碼外掛,使用者可以通過擴充套件的方式自定義協議的編碼和解碼;
  3. 提供攔截面,用於私有協議棧開發。

伺服器端最重要的設計原則有三條:
1. 服務端只提供上層的API,不與任何具體協議繫結;
2. 服務端提供給使用者的API要儘量遮蔽底層的通訊細節,防止底層的變更引起上層級聯變更。
3. 服務端功能上不要求全,重點在可擴充套件性上。

基於Netty開發通訊框架服務端,有以下幾個關鍵點需要掌握。
1. 用於接收客戶端連線的執行緒池:通常被稱為bossGroup,它的構造方法與處理I/O讀寫的執行緒池相同(workerGroup),都是通過new NioEventLoopGroup建立。bossGroup的執行緒數建議設定為1,因為它僅負責接收客戶端的連線,不做複雜的邏輯處理,為了儘可能少地佔用資源,它的取值越小越好。
2. TCP引數設定:建議 API層面開發TCP引數設定,為一些特殊的私有協議預留擴充套件點,對於大多數的開發者,使用預設值即可。Netty的ChannelOption類提供了對TCP引數的封裝。由於是工具常量類,未來發生變更的可能性很小,因此可以直接開放給上層使用。
3. 編解碼框架的定製:通訊框架封裝Netty的MessageToByteEncoder和LengthFieldBasedFrameDecoder,提供統一、通用的編解碼介面或者抽象類,由使用者實現編解碼介面,實現編解碼的靈活定製。
4. 通訊層業務邏輯的定製:利用Netty的ChannelPipeline,將ChannelHandler的鏈式編排能力以引數或者配置化的方式工發出來,由使用者靈活擴充套件,實現協議層邏輯定製。例如示例程式碼中的超時檢測、握手認證等。需要指出的是,如果擔心底層通訊框架API發生變更直接影響使用者的程式碼,可以在Netty的ChannelHandler上面再抽象包裝一層,通過Facade模式遮蔽底層的實現細節。

客戶端設計

基於Netty Client的客戶端建立流程:
1. 建立Bootstrap例項,Bootstrap是Socket客戶端建立工具類,使用者通過Bootstrap可以方便地建立Netty的客戶端併發起非同步TCP連線操作
2. 初始化TCP連線引數,設定編解碼Handler和其它業務Handler
3. 呼叫Bootstrap的connect方法非同步發起連線
4. 採用設定連線監聽器的方式,用於連線結果非同步通知
5. 服務端返回TCP握手應答,系統回撥監聽器操作完成介面
6. 在操作完成介面中實現相關業務邏輯,通知客戶端連線操作完成

可靠性設計

鏈路有效性檢測

心跳檢測機制,有三個層面
TCP層面
協議層的心跳檢測
應用層的心跳檢測
不同的協議,心跳檢測機制也存在差異,歸納起來主要分為兩類:
- Ping-Pong
- Ping-Ping
無論發生心跳超時還是心跳失敗,都需要關閉鏈路,由客戶端發起重連操作,保證鏈路能夠恢復正常。
Netty的心跳檢測實際上利用了鏈路空閒檢測機制實現的,它的空閒檢測機制分為三種:
- 讀空閒
- 寫空閒
- 讀寫空閒
Nety的預設讀寫空閒機制是發生超時異常,關閉連線。但是可以定製它的超時實現機制,以使支援不同的使用者場景。

斷連重連機制
訊息快取重發
資源優雅釋放

通過註冊JDK的ShutdownHook來實現,當系統接收到退出指令後,首先標記系統處於退出狀態,不再接收新的訊息,然後將積壓的訊息處理完,最後呼叫資源回收介面將資源銷燬,最後各執行緒退出執行。
通常優雅退出有個時間限制,如30s,如果到時間仍未退出,則由監控指令碼kill -9 pid,強制退出。
Netty提供了完善的優雅停機介面,通過呼叫相關介面,可以實現執行緒池、訊息佇列、Socket控制代碼、多路複用器等的資源釋放。

效能設計

效能差的三個可能原因:
- 網路傳輸方式問題
- 序列化效能差
- 執行緒模型問題
影響通訊效能三方面
- 傳輸
- 協議
- 執行緒

高效能之道

  • 非同步非阻塞通訊
  • 高效的I/O執行緒模型
  • 高效能的序列化框架

最佳實踐

誤區1:不指定執行緒池執行緒大小

EventLoopGroup bossGroup=new NioEventLoopGroup(執行緒池大小);

如果不指定執行緒池大小,Netty會優先使用-Dio.netty.eventLoopThreads指定的值,如果使用者沒有設定,預設採用CPU Core * 2。如果負載非常輕,可以設定為1即可。
建議配置值1<=N<=CPU Core。如果I/O邏輯複雜,建議根據實際效能測試結果逐步調大執行緒數,儘量不要使用系統預設值。

誤區2:I/O執行緒池使用不當,導致通訊執行緒膨脹