天天看点

HBase源码:HMaster启动过程调试HMastermain方法HMaster类图HMaster的构造方法run方法MasterFileSystem构造方法总结

版本: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类继承关系如下图:

HBase源码:HMaster启动过程调试HMastermain方法HMaster类图HMaster的构造方法run方法MasterFileSystem构造方法总结

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请求队列,逻辑如下:

–&gt;创建n个selector,和一个n个线程的readpool,n由<code>ipc.server.read.threadpool.size</code>决定,默认为10

–&gt;读取每个请求的头和内容,将内容放入priorityqueue中

启动一个responder线程,功能是将响应队列里的数据写给各个client的connection通道,逻辑如下:

–&gt;创建nio selector

–&gt;默认超时时间为15 mins

–&gt;依次将responsequeue中的内容写回各通道,并关闭连接,buffer=8k

–&gt;如果该请求的返回没有写完,则放回队列头,推迟再发送

–&gt;对于超时未完成的响应,丢弃并关闭相应连接

启动n(n默认为10)个handler线程,功能是处理请求队列,并将结果写到响应队列

–&gt;读取priorityqueue中的call,调用对应的call方法获得value,写回out并调用dorespond方法,处理该请求,并唤醒writable selector

–&gt;启动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: –&gt;先检查配置项 “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节点数&gt;=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

–&gt;依次检查每一个hlog目录,查看它所属的region server是否online,如果是则不需要做任何动作,region server自己会恢复数据,如果不是,则需要将它分配给其它的region server

–&gt;split是加锁操作:

–&gt; 创建一个新的hlogsplitter,遍历每一个server目录下的所有hlog文件,依次做如下操作。(如果遇到文件损坏等无法跳过的错误,配 置<code>hbase.hlog.split.skip.errors=true</code> 以忽略之)

–&gt;启动<code>hbase.regionserver.hlog.splitlog.writer.threads</code>(默认为3)个线程,共使用128mb内存,启动这些写线程

–&gt;先通过lease机制检查文件是否能够append,如果不能则死循环等待

–&gt;把hlog中的内容全部加载到内存中(内存同时被几个写线程消费))

–&gt;把有损坏并且跳过的文件移到<code>/hbase/.corrupt/</code>目录中

–&gt; 把其余己经处理过的文件移到<code>/hbase/.oldlogs</code>中,然后删除原有的server目录

–&gt; 等待写线程结束,返回新写的所有路径

–&gt;解锁

写线程逻辑:

–&gt;从内存中读出每一行数据的key和value,然后查询相应的region路径。如果该region路径不存在,说明该region很可能己经被split了,则不处理这部分数据,因为此时忽略它们是安全的。

–&gt;如果上一步能查到相应的路径,则到对应路径下创建”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并回收