概述
總體結構如下:

用戶端
子產品介紹
排程執行
該子產品主要提供接口轉換、錯誤重試、服務分組等能力;
1:接口轉換
服務層定義的服務接口與使用者層不同,比如put/delete/increment/append等操作底層都是調用的mutate接口,而batch相關的操作,無論是讀還是寫都調用multi接口;
轉換邏輯封裝為一個callable對象,交由RpcRetryingCaller處理;
2:錯誤重試
RpcRetryingCaller負責與服務代理子產品直接互動 ,以及錯誤時的重試;
3:服務分組
batch相關的操作可能會涉及到多個rs,需要按照rs進行分組,然後多線程并發請求,這些邏輯是在AsyncProcess中;
對于非batch類請求則直接使用RpcRetryingCaller進行調用,AsyncProcess的内部實際上也是依賴了該類來執行單個rs請求;
服務代理
服務代理通常叫stub,即樁的意思,其實作了與服務端同樣的接口;
對排程執行子產品而言,調用stub的方法就相當于調用遠端的服務,而不必關心實作細節;
這部分依賴protobuf元件,通過在proto檔案中定義service及message類型的參數,可直接生成接口和stub實作類;
在ConnectionImplementation類中有一個Map類型的stubs變量,其key為service name + regionserver,value則是stub執行個體;
通信子產品
該子產品主要進行序列化和io處理;
目前HBase已采用netty作為底層的io架構,用戶端的核心類為NettyRpcClient;
服務代理層的stub類中含有一個BlockingRpcChannel類型的變量,而rpcClient通過實作該接口并将執行個體注入來與之對接;
序列化則是依賴protobuf元件,序列化與反序列化的邏輯都放在NettyRpcDuplexHandler中,該類注冊在netty的pipeline,會基于不同的事件自動調用;
元件互動
這裡面整體調用脈絡比較清晰,值得注意的是,應用線程在調用底層io線程進行發送之前,會将請求相關的東西封裝到一個call對象裡面,然後将其暫存在一個id2call的map中,在拿到傳回結果時,根據結果資料中的callId,再從id2call中擷取對應的call對象,并喚醒應用線程;
線程互動
分為簡單請求和複雜請求兩種情況,差別在于是否使用額外的線程池;
簡單請求
非multi和scan;
複雜請求
multi和scan;
服務端
該子產品主要負責資料的讀取、反序列化并封裝為call對象;
核心實作類為NettyRpcServer,通過在pipeline中注冊的一些handler來完成上述處理;
資料流的格式大緻如下:
對其處理可分為3個階段:
1、讀取preamble(序文)
這是連接配接建立後,最先要發送的資料,共有6個位元組,格式為"HBasXX",後兩位為與版本和校驗相關的數字;
相關的handler為FixedLengthFrameDecoder和NettyRpcServerPreambleHandler,前者負責讀取定長位元組數,後者負責校驗内容;
2、讀取connectionHeader
該部分包含size和data,讀取完前述的preamble之後再讀取4個位元組即為size,轉換後的int即代表了data部分的長度;
connectionHeader用來對連接配接進行一些約定,比如請求的serviceName、編碼、壓縮及加密設定等,具體參見RPC.proto檔案中的ConnectionHeader;
相關的handler為NettyRpcFrameDecoder和NettyRpcServerRequestDecoder,前者負責讀取定長位元組得到size以及根據size讀取data,後者則通過調用ServerRpcConnection的processConnectionHeader方法進行進一步處理;
connectionHeader隻會發送和處理一次,後續的資料就都是request了;
3、讀取request
該部分的資料讀取部分與connectionHeader一緻,差別在于處理方法為ServerRpcConnection的processRequest;
其内部主要包含requestHeader和param兩部分;
requestHeader為單次請求的總體描述,比如請求的方法名、優先級、逾時時間等,具體參見RPC.proto檔案中的RequestHeader;
param為所請求方法的參數,比如GetRequest、MutateRequest等,具體參見Client.proto和HBase.proto檔案中的相關定義;
通信子產品得到的call對象會交由rpcScheduler進行排程,目前預設實作為SimpleRpcScheduler;
rpcScheduler的主要作用是根據請求類型把請求配置設定給不同的rpcExecutor執行個體,請求類型有3種:普通請求、高優先級請求和replication請求,而rpcExecutor的實作目前主要由RWQueueRpcExecutor和FastPathBalancedQueueRpcExecutor兩種,不同的類型使用了不同實作,關系如下:
RWQueueRpcExecutor的特點是内部可以對讀寫隔離,以及對get和scan隔離,所謂隔離的意思是,call對象會放入獨立的callQueue,并使用獨立的handler進行處理;
FastPathBalancedQueueRpcExecutor不支援隔離,其特點是對于空閑的handler,讓其自旋而不是阻塞,以減少線程上下文切換的消耗;
服務實作
服務端實作類需要實作一些接口,例如AdminService.BlockingInterface、ClientService.BlockingInterface、MasterService.BlockingInterface等;
HMaster的服務實作類主要是MasterRpcServices,HRegionServer的服務實作類主要是RSRpcServices;
service相關的類會在啟動階段進行初始化,然後在請求處理時根據connection的serviceName擷取到對應的service執行個體,再根據call對象的method和param進行方法的調用;
以HRegionServer為例,大概的service相關執行個體及調用關系如下: