HBase內部機制

一塵在心發表於2018-08-24

背景

在HMaster、RegionServer內部,建立了RpcServer例項,並與Client三者之間實現了Rpc呼叫,HBase0.95內部引入了Google-Protobuf作為中間資料組織方式,並在Protobuf提供的Rpc介面之上,實現了基於服務的Rpc實現,本文詳細闡述了HBase-Rpc實現細節。

HBase的RPC Protocol

 在HMaster、RegionServer內部,實現了rpc 多個protocol來完成管理和應用邏輯,具體如下protocol如下:

HMaster支援的Rpc協議:
MasterMonitorProtocol,Client與Master之間的通訊,Master是RpcServer端,主要實現HBase叢集監控的目的。

 

MasterAdminProtocol,Client與Master之間的通訊,Master是RpcServer端,主要實現HBase表格的管理。例如TableSchema的更改,Table-Region的遷移、合併、下線(Offline)、上線(Online)以及負載平衡,以及Table的刪除、快照等相關功能。

RegionServerStatusProtoco,RegionServer與Master之間的通訊,Master是RpcServer端,負責提供RegionServer向HMaster狀態彙報的服務。

RegionServer支援的Rpc協議:

ClientProtocol,Client與RegionServer之間的通訊,RegionServer是RpcServer端,主要實現使用者的讀寫請求。例如get、multiGet、mutate、scan、bulkLoadHFile、執行Coprocessor等。

AdminProtocols,Client與RegionServer之間的通訊,RegionServer是RpcServer端,主要實現Region、服務、檔案的管理。例如storefile資訊、Region的操作、WAL操作、Server的開關等。

(備註:以上提到的Client可以是使用者Api、也可以是RegionServer或者HMaster)

hbase-protorpc_1

 HBase-RPC實現機制分析

RpcServer配置三個佇列:

1)普通佇列callQueue,絕大部分Call請求存在該佇列中:callQueue上maxQueueLength為${ipc.server.max.callqueue.length},預設是${hbase.master.handler.count}*DEFAULT_MAX_CALLQUEUE_LENGTH_PER_HANDLER,目前0.95.1中,每個Handler上CallQueue的最大個數預設值(DEFAULT_MAX_CALLQUEUE_LENGTH_PER_HANDLER)為10。

2)優先順序佇列: PriorityQueue。如果設定priorityHandlerCount的個數,會建立與callQueue相當容量的queue儲存Call,該優先順序佇列對應的Handler的個數由rpcServer例項化時傳入。

3)拷貝佇列:replicationQueue。由於RpcServer由HMaster和RegionServer共用,該功能僅為RegionServer提供,queue的大小為${ipc.server.max.callqueue.size}指定,預設為1024*1024*1024,handler的個數為hbase.regionserver.replication.handler.count。

RpcServer由三個模組組成:

Listener ===Queue=== Responder

 hbase_rpc_95

這裡以HBaseAdmin.listTables為例,分析一個Rpc請求的函式呼叫過程:

1) RpcClient建立一個BlockingRpcChannel。

2)以channel為引數建立執行RPC請求需要的stub,此時的stub已經被封裝在具體Service下,stub下定義了可執行的rpc介面。

3)stub呼叫對應的介面,實際內部channel呼叫callBlockingMethod方法。

RpcClient內實現了protobuf提供的BlockingRpcChannel介面方法callBlockingMethod,

  @Overridepublic Message callBlockingMethod(MethodDescriptor md, RpcController controller,Message param, Message returnType)throws ServiceException {return this.rpcClient.callBlockingMethod(md, controller, param, returnType, this.ticket,this.isa, this.rpcTimeout);}

通過以上的實現細節,最終轉換成rpcClient的呼叫,使用MethodDescriptor封裝了不同rpc函式,使用Message基類可以接收基於Message的不同的Request和Response物件。

4)RpcClient建立Call物件,查詢或者建立合適的Connection,並喚醒Connection。

5)Connection等待Call的Response,同時rpcClient呼叫函式中,會使用connection.writeRequest(Call call)將請求寫入到RpcServer網路流中。

6)等待Call的Response,然後層層返回給更上層介面,從而完成此次RPC呼叫。

RPCServer收到的Rpc報文的內部組織如下:

Magic

(4Byte)

Version

(1Byte)

AuthMethod

(1Byte)

Connection

HeaderLength

(4Byte)

ConnectionHeader

Request

“HBas”

         

驗證RpcServer的CURRENT_VERSION

與RPC報文一致

目前支援三類:

AuthMethod.SIMPLE

AuthMethod.KERBEROS

AuthMethod.DIGEST

RPC.proto定義
RPCProtos.ConnectionHeader
message ConnectionHeader {
optional UserInformation userInfo = 1;
optional string serviceName = 2;
// Cell block codec we will use sending over optional cell blocks.  Server throws exception
// if cannot deal.
optional string cellBlockCodecClass = 3 [default = "org.apache.hadoop.hbase.codec.KeyValueCodec"];
// Compressor we will use if cell block is compressed.  Server will throw exception if not supported.
// Class must implement hadoop’s CompressionCodec Interface
optional string cellBlockCompressorClass = 4;
}
序列化之後的資料

     

整個Request儲存是經過編碼之後的byte陣列,包括如下幾個部分:

RequestHeaderLength(RawVarint32)

RequestHeader

ParamSize(RawVarint32)

Param

CellScanner

RPC.proto定義:
message RequestHeader {
// Monotonically increasing callId to keep track of RPC requests and their response
optional uint32 callId = 1;
optional RPCTInfo traceInfo = 2;
optional string methodName = 3;
// If true, then a pb Message param follows.
optional bool requestParam = 4;
// If present, then an encoded data block follows.
optional CellBlockMeta cellBlockMeta = 5;
// TODO: Have client specify priority
}
序列化之後的資料
並從Header中確認是否存在Param和CellScanner,如果確認存在的情況下,會繼續訪問。

Protobuf的基本型別Message,
Request的Param繼承了Message,
這個需要獲取的Method型別決定。

     

從功能上講,RpcServer上包含了三個模組,

1)Listener。包含了多個Reader執行緒,通過Selector獲取ServerSocketChannel接收來自RpcClient傳送來的Connection,並從中重構Call例項,新增到CallQueue佇列中。

 ”IPC Server listener on 60021″ daemon prio=10 tid=0x00007f7210a97800 nid=0x14c6 runnable [0x00007f720e8d0000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
- locked <0x00000000c43cae68> (a sun.nio.ch.Util$2)
- locked <0x00000000c43cae50> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000c4322ca8> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:84)
at org.apache.hadoop.hbase.ipc.RpcServer$Listener.run(RpcServer.java:646)

2)Handler。負責執行Call,呼叫Service的方法,然後返回Pair<Message,CellScanner>

“IPC Server handler 0 on 60021″ daemon prio=10 tid=0x00007f7210eab000 nid=0x14c7 waiting on condition [0x00007f720e7cf000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for  <0x00000000c43cad90> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:399)
at org.apache.hadoop.hbase.ipc.RpcServer$Handler.run(RpcServer.java:1804)

3) Responder。負責把Call的結果返回給RpcClient。

 ”IPC Server Responder” daemon prio=10 tid=0x00007f7210a97000 nid=0x14c5 runnable [0x00007f720e9d1000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method)
at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210)
at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65)
at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69)
- locked <0x00000000c4407078> (a sun.nio.ch.Util$2)
- locked <0x00000000c4407060> (a java.util.Collections$UnmodifiableSet)
- locked <0x00000000c4345b68> (a sun.nio.ch.EPollSelectorImpl)
at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80)
at org.apache.hadoop.hbase.ipc.RpcServer$Responder.doRunLoop(RpcServer.java:833)
at org.apache.hadoop.hbase.ipc.RpcServer$Responder.run(RpcServer.java:816)

RpcClient為Rpc請求建立Connection,通過Connection將Call傳送RpcServer,然後RpcClient等待結果的返回。

相關文章