HBase源码分析3—HMaster启动过程
Main Entry
首先找一下启动的入口。从HBase的启动脚本中可以找到类似
1 2 |
if [ "$COMMAND" = "master" ] ; then CLASS='org.apache.hadoop.hbase.master.HMaster' |
从这里确定了入口的包 org.apache.hadoop.hbase.master ,在 HMaster 类中有 main 方法
1 2 3 4 |
public static void main(String[] args) { VersionInfo.logVersion(); new HMasterCommandLine(HMaster.class).doMain(args); } |
HMasterCommandLine 继承自 ServerCommandLine ,doMain()执行的是基类的方法,借助 ToolRunner 执行了 HMasterCommandLine.run() 。
1 2 3 4 5 6 7 8 9 10 11 |
public void doMain(String args[]) { try { int ret = ToolRunner.run(HBaseConfiguration.create(), this, args); if (ret != 0) { System.exit(ret); } } catch (Exception e) { LOG.error("Failed to run", e); System.exit(-1); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public static int run(Configuration conf, Tool tool, String[] args) throws Exception{ if(conf == null) { conf = new Configuration(); } GenericOptionsParser parser = new GenericOptionsParser(conf, args); //set the configuration back, so that Tool can configure itself tool.setConf(conf); //get the args w/o generic hadoop args String[] toolArgs = parser.getRemainingArgs(); return tool.run(toolArgs); //这里,tool是doMain中传来的this,也就是HMasterCommandLine对象 } |
看下 HMasterCommandLine 做了什么。run方法做了一些参数检查的工作,最后调用了 startMaster()。
startMaster() 里分为两种模式,一种为’local’模式(即默认的standalone模式),在这种模式下会在本机JVM中同时启动master、Region server和Zookeeper,并在最后通过 LocalHBaseCluster 类对所有master、region server等创建现成和提供访问接口。
另一种模式就是distributed模式,就只会构造一个HMaster对象并调用 master.start()。
(注: HMaster.constructMaster 返回的实际上是HMaster的构造函数,start的是构造函数)
HMaster
init & wait
HMaster 继承自 HRegionServer,也有Region Server的功能,但是只存储关键的几个表。观察下HMaster.java,前400行基本都是定义了一些管理类,后面有空再慢慢分析这些类分别都是干啥的,先跳过。
HMaster的启动为两个部分,一部分是初始化 HRegionServer和启动 ActiveMasterManager ,这部分在HMaster的构造函数中完成,之后就会进入睡眠状态,等待被zk召唤成为primary master的那天,开始执行第二部分,执行 finishActiveMasterInitialization完成所有成员的初始化以及信息收集、节点管理等,正式接管master工作。
我们先看第一部分, HMaster 的构造函数,初始化了一堆配置然后启动了ActiveMasterManager
1 2 3 |
activeMasterManager = new ActiveMasterManager(zooKeeper, this.serverName, this); int infoPort = putUpJettyServer(); startActiveMasterManager(infoPort); |
ActiveMasterManager 这个对象是用于处理master选举过程的,负责与ZK进行通信,一旦确认成为primary master,会执行 finishActiveMasterInitialization 完成master启动流程。
跟一下 startActiveMasterManager 这个方法,内部先在ZK注册了一下自己为backup master身份,然后启动了一个线程,会hang在这里,等待召唤
1 2 3 |
if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) { finishActiveMasterInitialization(status); } |
blockUntilBecomingActiveMaster 里面是一个while循环,不断尝试注册成为主节点,一但成功就退出。
下面我们看看第二部分,当HMaster在ZK注册为primary master后做了什么事。
首先启动了一个监视器 zombieDetector , InitializationMonitor 对象监控master是否在配置的规定时间内初始化完毕,否则认为master启动失败,将会主动退出。
接下来初始化了若干必须的服务,如 fileSystemManager 、 tableLockManager 等,还有若干tracker,他们具体的内容后面我们慢慢研究。
接下来,新的master已选出,整个集群中的region server需要来注册:
1 2 3 4 5 6 7 8 9 10 |
// Wait for region servers to report in this.serverManager.waitForRegionServers(status); // Check zk for region servers that are up but didn't register for (ServerName sn : this.regionServerTracker.getOnlineServers()) { // The isServerOnline check is opportunistic, correctness is handled inside if (!this.serverManager.isServerOnline(sn) && serverManager.checkAndRecordNewServer(sn, ServerLoad.EMPTY_SERVERLOAD)) { LOG.info("Registered server found up in zk but who has not yet reported in: " + sn); } } |
waitForRegionServers返回有三个条件(需同时满足):
- 自上一个region server注册后一个周期时间内没有新的region server注册
- 总时间超时
- 注册数量达到配置的最少节点数
如果没有满足,则会一直等待region server过来注册。(极端情况如果hang死这这个地方,会被 InitializationMonitor 干掉,前提是设置了master启动的timeout)
显然这样不能保证所有的region server都过来注册过了,所以还得检查下是谁没来注册,打个log。
Log Splitting
接下来进行的任务是log splitting,因为HBase使用WAL机制,所以当机器宕机的时候内存中未落盘的数据会丢失,但是在WAL日志中可以找到记录做replay。由于每个region server管理多个region,把某个丢数据的region的日志从WAL日志中分离出来的过程就叫log splitting。
为了让master尽快启动,在master初始化阶段只进行hbase:mata的log splitting,
1 2 3 4 5 6 7 8 9 |
Set<ServerName> previouslyFailedServers = this.fileSystemManager.getFailedServersFromLogFolders(); ServerName oldMetaServerLocation = metaTableLocator.getMetaRegionLocation(this.getZooKeeper()); if (oldMetaServerLocation != null && previouslyFailedServers.contains(oldMetaServerLocation)) { splitMetaLogBeforeAssignment(oldMetaServerLocation); // splitting here } Set<ServerName> previouslyFailedMetaRSs = getPreviouselyFailedMetaServersFromZK(); previouslyFailedMetaRSs.addAll(previouslyFailedServers); |
首先看一下 getFailedServersFromLogFolders , 简单说就是去WALs目录下翻日志,关键代码我选一下帖
1 2 3 4 5 6 7 8 |
FileStatus[] logFolders = FSUtils.listStatus(this.walFs, logsDirPath, null); for (FileStatus status : logFolders) { /* check if log contains content */ final ServerName serverName = DefaultWALProvider.getServerNameFromWALDirectoryName(status.getPath()); if (!onlineServers.contains(serverName)) { serverNames.add(serverName); } } |
之前说了master只会做hbase:meta的log splitting,所以要先检查下meta region是不是在failedServer列表里,如果在,那么就进行 splitMetaLogBeforeAssignment分离meta的log。跟进去看下:
1 2 3 4 5 6 7 8 9 10 11 |
private void splitMetaLogBeforeAssignment(ServerName currentMetaServer) throws IOException { if (RecoveryMode.LOG_REPLAY == this.getMasterFileSystem().getLogRecoveryMode()) { // In log replay mode, we mark hbase:meta region as recovering in ZK Set<HRegionInfo> regions = new HashSet<HRegionInfo>(); regions.add(HRegionInfo.FIRST_META_REGIONINFO); this.fileSystemManager.prepareLogReplay(currentMetaServer, regions); } else { // In recovered.edits mode: create recovered edits file for hbase:meta server this.fileSystemManager.splitMetaLog(currentMetaServer); } } |
一种是ZK模式,一种是通过文件恢复模式。有点复杂,后面研究恢复和WAL的时候细说吧。
Assignment
然后初始化balancer。assign meta
1 |
assignMeta(status, previouslyFailedMetaRSs, HRegionInfo.DEFAULT_REPLICA_ID); |
如果assign过了,我们就默默记下就好了。没assign过,要assign一下。在当前版本中, hbase.assignment.usezk 默认是true,可以通过ZK来assign region,一行代码就搞定了。否则我们需要检查一下是不是分配成功,不行的话调用 assignmentManager.assignMeta(hri) 再来一次。。
之后调用 enableMeta(TableName.META_TABLE_NAME) 启用meta。
meta分配完了还得分配存储数据的region,入口是 this.assignmentManager.joinCluster() ,会扫描meta表重建存储结构。
最后
然后后面启动一堆定时操作,balanceChore啥的。
初始化命名空间 initNamespace();
分配meta replica。
一些清理工作。
关闭master启动的监视器,master启动成功。
博客大好