版本:hbase 0.94.15-cdh4.7.0
說明:
首先,在ide裡啟動hmaster和hregionserver:
運作<code>/hbase/src/test/java/my/test/start/hmasterstarter.java</code>,當看到提示<code>waiting for region servers count to settle</code>時, 再打開同目錄中的hregionserverstarter,統一運作該類。
此時會有兩個console,在hmasterstarter這個console最後出現<code>master has completed initialization</code>,這樣的資訊時就表示它啟動成功了,而hregionserverstarter這個console最後出現<code>done with post open deploy task</code>這樣的資訊時說明它啟動成功了。
運作hmasterstarter類啟動hmaster:
hmaster的入口是main方法,main方法需要傳遞一個參數,start或者stop。
main方法内首先列印hbase版本資訊,然後在調用hmastercommandline的domain方法。hmastercommandline繼承自servercommandline類并且servercommandline類實作了tool接口。
domain方法内會調用toolrunner的run方法,檢視toolrunner類可以知道,實際上最後會調用hmastercommandline的run方法。
接下來會解析參數,根據參數值判斷是執行startmaster方法還是stopmaster方法。
startmaster方法中分兩種情況:本地模式和分布式模式。如果是分布式模式,通過反射調用hmaster的構造方法,并調用其start和join方法。
hmaster繼承自hasthread類,而hasthread類實作了runnable接口,故hmaster也是一個線程。
hmaster類繼承關系如下圖:

1、構造方法總體過程
建立configuration并設定和擷取一些參數。包括:
在master上禁止block cache
設定服務端重試次數
擷取主機名稱和master綁定的ip和端口号,端口号預設為60000
設定regionserver的coprocessorhandler線程數為0
建立rpcserver(見下文分析)
初始化servername,其值為:<code>192.168.1.129,60000,1404117936154</code>
zk授權登入和hbase授權
設定目前線程名稱:<code>master + "-" + this.servername.tostring()</code>
判斷是否開啟複制:<code>replication.decoratemasterconfiguration(this.conf);</code>
設定<code>mapred.task.id</code>,如果其為空,則其值為:<code>"hb_m_" + this.servername.tostring()</code>
建立zookeeperwatcher監聽器(見下文分析),并在zookeeper上建立一些節點
啟動rpcserver中的線程
建立一個mastermetrics
判斷是否進行健康檢測:healthcheckchore
另外還初始化兩個參數:shouldsplitmetaseparately、waitingonlogsplitting
涉及到的參數有:
2、建立rpcserver并啟動其中的線程:
這部分涉及到rpc的使用,包括的知識點有<code>動态代理</code>、<code>java nio</code>等。
通過反射建立rpcengine的實作類,實作類可以在配置檔案中配置(<code>hbase.rpc.engine</code>),預設實作為writablerpcengine。 調用getserver方法,其實也就是new一個hbaseserver類。
構造方法中:
啟動一個listener線程,功能是監聽client的請求,将請求放入nio請求隊列,邏輯如下:
–>建立n個selector,和一個n個線程的readpool,n由<code>ipc.server.read.threadpool.size</code>決定,預設為10
–>讀取每個請求的頭和内容,将内容放入priorityqueue中
啟動一個responder線程,功能是将響應隊列裡的資料寫給各個client的connection通道,邏輯如下:
–>建立nio selector
–>預設逾時時間為15 mins
–>依次将responsequeue中的内容寫回各通道,并關閉連接配接,buffer=8k
–>如果該請求的傳回沒有寫完,則放回隊列頭,推遲再發送
–>對于逾時未完成的響應,丢棄并關閉相應連接配接
啟動n(n預設為10)個handler線程,功能是處理請求隊列,并将結果寫到響應隊列
–>讀取priorityqueue中的call,調用對應的call方法獲得value,寫回out并調用dorespond方法,處理該請求,并喚醒writable selector
–>啟動m(m預設為0)個handler線程以處理priority
3、建立zookeeperwatcher
構造函數中生成如下持久節點:
接下來看hmaster的run方法做了哪些事情。
1、總體過程
建立monitoredtask,并把hmaster的狀态設定為master startup
啟動info server,即jetty伺服器,端口預設為60010,其對外提供兩個接口:/master-status和/dump
調用becomeactivemaster方法(見下文分析),阻塞等待直至目前master成為active master
當成為了master之後并且目前master程序正在運作,則調用finishinitialization方法(見下文分析),并且調用loop方法循環等待,一直到stop發生
當hmaster停止運作時候,會做以下事情:
清理startupstatus
停止balancerchore和catalogjanitorchore
讓regionservers shutdown
停止服務線程:rpcserver、logcleaner、hfilecleaner、infoserver、executorservice、healthcheckchore
停止以下線程:activemastermanager、catalogtracker、servermanager、assignmentmanager、filesystemmanager、snapshotmanager、zookeeper
2、becomeactivemaster方法:
建立activemastermanager
zookeeperwatcher注冊activemastermanager監聽器
調用stallifbackupmaster: –>先檢查配置項 “hbase.master.backup”,自己是否backup機器,如果是則直接block直至檢查到系統中的active master挂掉(<code>zookeeper.session.timeout</code>,預設每3分鐘檢查一次)
建立clusterstatustracker并啟動
調用activemastermanager的blockuntilbecomingactivemaster方法。
建立短暫的”/hbase/master”,此節點值為version+servername,如果建立成功,則删除備份節點;否則,建立備份節點
獲得”/hbase/master”節點上的資料,如果不為null,則獲得servername,并判斷是否是在目前節點上建立了”/hbase/master”,如果是則删除該節點,這是因為該節點已經是備份節點了。
3、finishinitialization方法:
建立masterfilesystem對象,封裝了master常用的一些檔案系統操作,包括splitlog file、删除region目錄、删除table目錄、删除cf目錄、檢查檔案系統狀态等.
建立fstabledescriptors對象
設定叢集id
如果不是備份master:
建立executorservice,維護一個executormap,一種event對應一個executor(線程池).可以送出eventhandler來執行異步事件; - 建立servermanager,管理regionserver資訊,維護着onlineregion server 和deadregion server清單,處理regionserver的startups、shutdowns、 deaths,同時也維護着每個regionserver rpc stub.
調用initializezkbasedsystemtrackers,初始化zk檔案系統
建立catalogtracker, 它包含rootregiontracker和metanodetracker,對應”/hbase/root-region-server”和/”hbase/unassigned/1028785192”這兩個結點(1028785192是.meta.的分區名)。如果之前從未啟動過hbase,那麼在start catalogtracker時這兩個結點不存在。”/hbase/root-region-server”是一個持久結點,在rootlocationeditor中建立
建立 loadbalancer,負責region在regionserver之間的移動,關于balancer的政策,可以通過hbase.regions.slop來設定load區間
建立 assignmentmanager,負責管理和配置設定region,同時它也會接受zk上關于region的event,根據event來完成region的上下線、關閉打開等工作。
建立 regionservertracker: 監控”/hbase/rs”結點,通過zk的event來跟蹤onlineregion servers, 如果有rs下線,删除servermanager中對應的onlineregions.
建立 drainingservertracker: 監控”/hbase/draining”結點
建立 clusterstatustracker,監控”/hbase/shutdown”結點維護叢集狀态
建立snapshotmanager
如果不是備份master,初始化mastercoprocessorhost并執行startservicethreads()。說明:<code>info server的啟動移到構造函數了去了,這樣可以早點通過jetty伺服器檢視hmaster啟動狀态。</code>
建立一些executorservice
建立logcleaner并啟動
建立hfilecleaner并啟動
啟動healthcheckchore
打開rpcserver
等待regionserver注冊。滿足以下這些條件後傳回目前所有region server上的region數後繼續:
a 至少等待4.5s,”hbase.master.wait.on.regionservers.timeout”
b 成功啟動regionserver節點數>=1,”hbase.master.wait.on.regionservers.mintostart”
c 1.5s内沒有regionsever死掉或重新啟動,<code>hbase.master.wait.on.regionservers.interval</code>)
servermanager注冊新的線上region server
如果不是備份master,啟動assignmentmanager
擷取下線的region server,然後拆分hlog
–>依次檢查每一個hlog目錄,檢視它所屬的region server是否online,如果是則不需要做任何動作,region server自己會恢複資料,如果不是,則需要将它配置設定給其它的region server
–>split是加鎖操作:
–> 建立一個新的hlogsplitter,周遊每一個server目錄下的所有hlog檔案,依次做如下操作。(如果遇到檔案損壞等無法跳過的錯誤,配 置<code>hbase.hlog.split.skip.errors=true</code> 以忽略之)
–>啟動<code>hbase.regionserver.hlog.splitlog.writer.threads</code>(預設為3)個線程,共使用128mb記憶體,啟動這些寫線程
–>先通過lease機制檢查檔案是否能夠append,如果不能則死循環等待
–>把hlog中的内容全部加載到記憶體中(記憶體同時被幾個寫線程消費))
–>把有損壞并且跳過的檔案移到<code>/hbase/.corrupt/</code>目錄中
–> 把其餘己經處理過的檔案移到<code>/hbase/.oldlogs</code>中,然後删除原有的server目錄
–> 等待寫線程結束,傳回新寫的所有路徑
–>解鎖
寫線程邏輯:
–>從記憶體中讀出每一行資料的key和value,然後查詢相應的region路徑。如果該region路徑不存在,說明該region很可能己經被split了,則不處理這部分資料,因為此時忽略它們是安全的。
–>如果上一步能查到相應的路徑,則到對應路徑下建立”recovered.edits”檔案夾(如果該檔案夾存在則删除後覆寫之),然後将資料寫入該檔案夾
調用assignroot方法,檢查是否配置設定了-root-表,如果沒有,則通過assignmentmanager.assignroot()來配置設定root表,并激活該表
運作this.servermanager.enablesshforroot()方法
拆分.meta. server上的hlog
配置設定.meta.表
enableservershutdownhandler
處理dead的server
assignmentmanager.joincluster();
設定balancer
fixupdaughters(status)
如果不是備份master
啟動balancerchore線程,運作loadbalancer
啟動startcatalogjanitorchore,周期性掃描<code>.meta.</code>表上未使用的region并回收
registermbean
servermanager.cleardeadserverswithsamehostnameandportofonlineserver,清理dead的server
如果不是備份master,cphost.poststartmaster
在<code>hmaster.finishinitialization</code>方法中觸發了masterfilesystem的構造方法,該類在hmaster類中會被以下類使用:
logcleaner
hfilecleaner
另外該類可以完成拆分log的工作:
這裡主要是關心建立了哪些目錄,其他用途暫不分析。
1、接下來,看其構造方法運作過程:
擷取rootdir:由參數<code>hbase.rootdir</code>配置
擷取tempdir:<code>${hbase.rootdir}/.tmp</code>
擷取檔案系統的uri,并設定到<code>fs.default.name</code>和<code>fs.defaultfs</code>
判斷是否進行分布式檔案拆分,參數:<code>hbase.master.distributed.log.splitting</code>,如果需要,則建立splitlogmanager
建立oldlogdir,調用createinitialfilesystemlayout方法
checkrootdir
等待fs退出安全模式(預設10秒鐘輪循一次,可通過參數<code>hbase.server.thread.wakefrequency</code>調整
如果hbase.rootdir目錄不存在則建立它,然後在此目錄中建立名為”hbase.version”的檔案,内容是檔案系統版本号,目前為7;如果hbase.rootdir目錄已存在,則讀出”hbase.version”檔案的内容與目前的版本号相比,如果不相等,則列印錯誤資訊(提示版本不對),抛出異常filesystemversionexception
檢查<code>${hbase.rootdir}</code>目錄下是否有名為”hbase.id”的檔案,如果沒有則建立它,内容是随機生成的uuid(總長度36位,由5部份組成,用”-“分隔),如:6c43f934-37a2-4cae-9d49-3f5abfdc113d
讀出”hbase.id”的檔案的内容存到clusterid字段
判斷hbase.rootdir目錄中是否有”-root-/70236052”目錄,沒有的話說明是第一次啟動hbase,進入bootstrap方法
createroottableinfo 建立”-root-“表的描述檔案,判斷<code>hbase.rootdir/-root-</code>目錄中是否存在tableinfo開頭的檔案,另外還建立了.tmp目錄
checktempdir
如果oldlogdir(<code>${hbase.rootdir}/.oldlogs</code>)不存在,則建立
2、bootstrap方法運作過程:
調用hregion.createhregion建立”-root-“分區和”.meta.”分區
把”.meta.”分區資訊加到”-root-“表,并關閉分區和hlog
經過上面分析之後,來看看zookeeper建立的一些目錄分布式由哪個類來監控的:
<code>/hbase</code>
<code>/hbase/root-region-server</code>:rootregiontracker,監控root所在的regionserver
<code>/hbase/rs</code>:regionservertracker,監控regionserver的上線和下線
<code>/table/draining</code>:drainingservertracker,監聽regionserver清單的變化
<code>/hbase/master</code>:在hmaster中建立,并且是一個短暫結點,結點的值是hmaster的servername:<code>hostname,port,目前毫秒</code>
<code>/hbase/backup-masters</code>
<code>/hbase/shutdown</code>:clusterstatustracker,當hmaster啟動之後,會将目前時間(<code>bytes.tobytes(new java.util.date().tostring())</code>)存到該節點
<code>/hbase/unassigned</code>:metanodetracker
<code>/hbase/table94</code>
<code>/hbase/table</code>
<code>/hbase/hbaseid</code>:在<code>hmaster.finishinitialization</code>方法中調用clusterid.setclusterid建立,結點值是uuid
<code>/hbase/splitlog</code>
在hmaster啟動之後,<code>${hbase.rootdir}</code>目錄如下:
簡單總結一下hmaster啟動過程做了哪些事情:
建立rpcserver,及hbaseserver
建立zookeeperwatcher監聽器
阻塞等待成為activemaster
建立master的一些檔案目錄
初始化一些基于zk的跟蹤器
建立loadbalancer
建立jetty的infoserver并啟動
啟動健康檢查
等待regionserver注冊
從hlog中恢複資料
配置設定root和meta表
配置設定region
運作負載均衡線程
周期性掃描.meta.表上未使用的region并回收