在我的博文《HBase——HMaster啟動之一》、《HBase——HMaster啟動之二》中已經詳細介紹過HMaster在啟動過程中調用的各種方法。下面,單就HRegionServer在啟動過程中與HMaster的交互做一下深入分析。
首先,讓我們來到HRegionServer.run,由於其也是間接實現了Runnable接口。因此,在這里,就從他的run方法開始分析。對於前面的preRegistrationInitialization方法我在這里並不打算講,因為在我的博文《HBase——HMaster啟動之一》中已經詳細描述過。這里,就后面的兩個方法reportForDuty、handleReportForDutyResponse進行詳細描述。
讓我們來到reportForDuty方法中。在下圖中我已經將其中的重點框選出來。其中的第一個方法是HRegionServer作為與HMaster交互的重點。我在本節將詳細講述。至於第二個方法,相信我在仔細描述第一個方法之后,大家就會感覺清晰明了。首先來到createRegionServerStatusStub方法。由於是長圖,所以沒有辦法編輯,還請大家諒解。
首先,來到masterAddressTracker.getMasterAddress方法。這個MasterAddressTracker在HRegionServer構造時創建並啟動,他所監聽的ZK上的路徑是/hasse/master。refresh為true,這里強制去ZK上查詢HMaster的信息。然后調用了rpcClient.createBlockingRpcChannel。這里的rpcClient是在preRegistrationInitialization方法中構造的。
由於在rpcClient.createBlockingRpcChannel方法中僅僅返回了新構造的BlockingRpcChannelImplementation,因此,讓我們來到BlockingRpcChannelImplementation的構造方法。這里,我特別標明了BlockingRpcChannel,這個類的完整名稱是org.apache.hbase.thirdparty.com.google.protobuf.BlockingRpcChannel。這是由於hbase中將protobuf加入了其第三方包中。其真正類名應該是com.google.protobuf.BlockingRpcChannel。不論如何,他都是PB中的一個接口。這里的BlockingRpcChannelImplementation重寫了接口中的callBlockingMethod方法,也就是說,在實際調用的時候,真正調用了rpcClient.callBlockingMethod方法。
接着,我們回到HRegionServer.createRegionServerStatusStub方法中。由上面的那張長圖可以看到,由rpcClient.createBlockingRpcChannel返回的對象傳入了RegionServerStatusService.newBlockingStub、LockService.newBlockingStub中,構造了這兩個service的本地stub。這是PB的基礎用法,與HDFS中對於PB的使用方式不用。這里封裝的並不是很好。接着,將這兩個service賦給HRegionServer的成員變量。




然后,我們就可以回到reportForDuty方法的第二個重點方法this.rssStub.regionServerStartup。這里的rssStub就是剛剛構建的service。其在具體方法調用的時候,正是通過BlockingRpcChannelImplementation中復寫的方法來實現的。
也就是說在實際調用rssStub.regionServerStartup,調用到了AbstractRpcClient.callBlockingMethod。關於調用的詳細流程,我在博文《hbase之RPC調用流程簡介》中有簡單介紹,如果大家感覺不是很詳細,可以私信我的163郵箱15935152719@163.com。我然后再寫一篇博文來詳述。這里我們假定這個請求已經發送到了服務端。且服務端的MasterRpcServices.regionServerStartup已經開始調用。
接下來,我們到達HMaster端的MasterRpcServices.regionServerStartup。如下圖所示,這里主要調用了ServerManager.regionServerStartup。
讓我們繼續深入ServerManager.regionServerStartup,由於其只是調用了ServerManager.checkAndRecordNewServer方法,並沒有其它重點內容,我在這里就不貼圖了。而是來到ServerManager.checkAndRecordNewServer。如下圖所示。findServerWithSameHostnamePortWithLock主要是查看當前已注冊的RegionServer中是否有同名的ServerName,如果有,則返回ServerName,如果沒有,則返回null。然后調用recordNewServerWithLock方法,將其ServerName放入成員變量onlineServers中,也就是說,該RegionServer已經實現了HMaster端的注冊。接着調用已注冊listeners的serverAdded方法,這些listeners都實現了ServerListener。主要有三個實現ServerListener接口的類:AssignmentManager、RSProcedureDispatcher、DrainingServerTracker。其中DrainingServerTracker是在其start方法中構造的匿名類。
至此,HMaster端實現與HRegionServer的第一次通信。接着,HRegionServer開始調用HRegionServe.handleReportForDutyResponse方法。如下圖所示。一開始的for循環是為了將HMaster端返回的部分信息添加到當前的conf成員變量中。緊接着createMyEphemeralNode方法將當前RegionServer的節點信息寫入ZK中,路徑為/hbase/rs/~。然后調用ZNodeClearer.writeMyEphemeralNodeOnDisk將信息本地化。再然后調用了setupWALAndReplication、startReplicationService兩個方法。由於setupWALAndReplication方法比較重要,我將在后面貼圖詳細介紹。
首先,我們來到setupWALAndReplication方法。在setupWALAndReplication方法中,主要做了兩件事情,其一是構造WALFactory,另外一個就是調用createNewReplicationInstance。



在構造WALFactory過程中,主要通過反射了AsyncFSWALProvider並將其封裝到SyncReplicationWALProvider中,然后調用其init方法。
在createNewReplicationInstance方法中主要通過反射方法創建了Replication,並調用了其initialize方法。並將新構建的Replication賦給了HRegionServer.replicationSourceHandler與HRegionServer.replicationSinkHandler。由於這里的Replication比較重要,我將在下面詳細介紹。
接下來,讓我們來到Replication.initialize方法,如下圖所示。方法很長,但重點只是構造了ReplicationSourceManager。而在ReplicationSourceManager的構造方法也只是將入參封裝,並構建了一個線程池。
接下來,來到handleReportForDutyResponse中第二個重要也是最重要的方法startServices。這個方法很長,幾乎所有的RegionServer的服務都是直接或間接通過這個方法啟動的。
首先,我們來看initializeThreads。



這里首先構造了MemStoreFlusher。在構造MemStoreFlusher時,值得我們注意的是,實例化了FlushHandler。接下來構實例化了CompactSplit。在構造CompactSplit的過程中,有值得我們關注的地方。下面我們跳過這張圖,來到CompactSplit的構造方法。
來到CompactSplit的構造方法以及他在構造方法中主要調用的兩個方法。在他的createCompactionExecutors方法中構造了一個StealJobQueue對象。這個類繼承自PriorityBlockingQueue,並且內部有一個BlockingQueue類型的stealFromQueue成員變量。然后構建了兩個線程池,分別以StealJobQueue、StealJobQueue.stealFromQueue為線程池的工作隊列。

接下來構建了一個PressureAwareCompactionThroughputController,並調用了其setup方法。由於其實現了Configurable接口,所以在通過ReflectionUtils.newInstance實例化的時候,調用了其setConf方法。在調用其setup時,實例化並調用了一個ScheduledChore對象,該對象在其復寫的chore方法中調用了PressureAwareCompactionThroughputController.tune。
介紹完CompactSplit的實例化后,讓我們繼續回到HRegionServer.initializeThreads方法中。在方法內,接着構造了CompactionChecker、PeriodicMemStoreFlusher這兩個ScheduledChore。接下來構造了Leases(看到這里,你是不是很容易想到HDFS的租約系統LeaseManager)。他擴展了Thread而不是Chore,因為當有事情要做時,睡眠時間可以被中斷,而不是Chore睡眠時間是不變的。

緊接着,分別調用MovedRegionsCleaner.create、nonceManager.createCleanupScheduledChore創建了兩個ScheduledChore,在二者的chore方法中調用的方法分別是:regionServer.cleanMovedRegions、ServerNonceManager.cleanUpOldNonces。
然后構造了RegionServerRpcQuotaManager與RegionServerSpaceQuotaManager,最后調用了registerConfigurationObservers,將相關的ConfigurationObserver注冊到ConfigurationManager中。
介紹完initializeThreads方法后,讓我們回到startServices方法。
在startServices方法中接着構建了SecureBulkLoadManager並調用了其start方法。在其start方法中在hadoop上創建了/hbase.rootdir/staging目錄。
接着構建了LogRoller,他的作用是定期運行以確定是否應該滾動WAL。同樣,他繼承了Thread而不是Chore,因為當有事情要做時,睡眠時間可以被中斷,而不是Chore睡眠時間是不變的。
然后方法FlushThroughputControllerFactory.create的調用構造了NoLimitThroughputController。
接着構建了RemoteProcedureResultReporter與CompactedHFilesDischarger(繼承自ScheduledChore)。
然后呢,就像HMaster在startServiceThreads方法中一樣,創建了一大堆有不同用途的線程池,由ExecutorService管理(注:這里的ExecutorService是org.apache.hadoop.hbase.executor.ExecutorService)。
緊接着,調用了walRoller、cacheFlusher、procedureResultReporter線程的start方法。這里簡單介紹一下cacheFlusher.start。其中cacheFlusher是我們在initializeThreads方法中構造的,他的類型是MemStoreFlusher,其成員變量flushHandlers在構造期間已經完成初始化。在這里完成了賦值與線程的啟動。
再然后,啟動了this.leases線程。構造並啟動了SplitLogWorker。
最后,分別調用了startHeapMemoryManager、initializeMemStoreChunkCreator。關於initializeMemStoreChunkCreator方法的調用,我在HMaster啟動那一節已經介紹過,這里就略過了。需要注意的是在構建ChunkCreator時創建MemStoreChunkPool都注冊到了startHeapMemoryManager方法創建的hMemManager,也就是交由hMemManager管理。所以,這里只是介紹startHeapMemoryManager。如下圖所示,調用HeapMemoryManager.create創建了HeapMemoryManager。然后調用HeapMemoryManager.start方法創建了HeapMemoryTunerChore並將其加入到ChoreService中調用。值得注意的是,在構建HeapMemoryTunerChore時,其內部的成員變量heapMemTuner類型為DefaultHeapMemoryTuner,並且,這個類實現了Configurable接口。當然他也是通過ReflectionUtils.newInstance方法創建的。
到此為止,我們已經介紹完了上面所有貼圖。下面,讓我們回到HRegionServer.run方法的后半部分。在這里,已經接近結束了。我只是簡單介紹一下。

大家可能對這里的rspmHost很迷惑,這個是什么時候出現的。確實,我之前並沒有介紹過,在這里補充一下。在HRegionServer.initializeZooKeeper方法的最后,構建了RegionServerProcedureManagerHost,並將其賦給了成員變量rspmHost。接着,便調用了rspmHost.loadProcedures、rspmHost.initialize。實現了他的初始調用。
來到最后的tryRegionServerReport,HRegionServer正是通過這個方法與HMaster保持心跳。
本節到這里就結束了,大家有什么不懂的地方可以私信我的郵箱15935152719@163.com。如果感覺不錯,希望留下你的贊。你的肯定是小編繼續前進的動力。
