HBase源码分析3—HMaster启动过程

25. 十二月 2017 hbase, 数据库 2

Main Entry

首先找一下启动的入口。从HBase的启动脚本中可以找到类似

if [ "$COMMAND" = "master" ] ; then
  CLASS='org.apache.hadoop.hbase.master.HMaster'

从这里确定了入口的包org.apache.hadoop.hbase.master ,在HMaster 类中有main 方法

public static void main(String[] args) {
    VersionInfo.logVersion();
    new HMasterCommandLine(HMaster.class).doMain(args);
}

HMasterCommandLine 继承自ServerCommandLine ,doMain()执行的是基类的方法,借助ToolRunner 执行了HMasterCommandLine.run() 。

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);
    }
}
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

    activeMasterManager = new ActiveMasterManager(zooKeeper, this.serverName, this);
    int infoPort = putUpJettyServer();
    startActiveMasterManager(infoPort);

ActiveMasterManager 这个对象是用于处理master选举过程的,负责与ZK进行通信,一旦确认成为primary master,会执行finishActiveMasterInitialization 完成master启动流程。

跟一下startActiveMasterManager 这个方法,内部先在ZK注册了一下自己为backup master身份,然后启动了一个线程,会hang在这里,等待召唤

    if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) { 
        finishActiveMasterInitialization(status); 
    }

blockUntilBecomingActiveMaster 里面是一个while循环,不断尝试注册成为主节点,一但成功就退出。

 

下面我们看看第二部分,当HMaster在ZK注册为primary master后做了什么事。

首先启动了一个监视器zombieDetectorInitializationMonitor 对象监控master是否在配置的规定时间内初始化完毕,否则认为master启动失败,将会主动退出。

接下来初始化了若干必须的服务,如fileSystemManagertableLockManager 等,还有若干tracker,他们具体的内容后面我们慢慢研究。

接下来,新的master已选出,整个集群中的region server需要来注册:

    // 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,

    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目录下翻日志,关键代码我选一下帖

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。跟进去看下:

    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

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启动成功。


2 thoughts on “HBase源码分析3—HMaster启动过程”

  • 1
    Anonymous on 2017年12月28日 回复

    博客大好

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据