博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hbase源码系列(六)HMaster启动过程
阅读量:6914 次
发布时间:2019-06-27

本文共 10822 字,大约阅读时间需要 36 分钟。

  这一章是server端开始的第一章,有兴趣的朋友先去看一下,我专门从网上弄下来的。

  按照HMaster的run方法的注释,我们可以了解到它的启动过程会去做以下的动作。

* 
  • 阻塞直到变成ActiveMaster *
  • 结束初始化操作 *
  • 循环 *
  • 停止服务并执行清理操作*
  •   HMaster是没有单点问题是,因为它可以同时启动多个HMaster,然后通过zk的选举算法选出一个HMaster来。

      我们首先来看看这个阻塞直到变成ActiveMaster的过程吧。

      1、如果不是master的话,就一直睡,isActiveMaster的判断条件是,在zk当中没有master节点,如果没有就一直等待。master节点建立之后,就开始向Master冲刺了,先到先得。

      2、尝试着在master节点下面把自己的ServerName给加上去,如果加上去了,它就成为master了,成为master之后,就把自己从备份节点当中删除。

      3、如果没有成为master,把自己添加到备份节点,同时检查一下当前的master节点,如果是和自己一样,那就是出现异常了,明明是设置成功了,确说不成功,接下来它就会一直等待,等到master死掉。

      成为master之后的结束初始化操作,这才是重头戏啊,前面的都是小意思,实例化的代码我就补贴了,看着也没啥意思,就把这些属性贴出来吧,让大家认识认识。

    /** 专门负责master和hdfs交互的类  */  private MasterFileSystem fileSystemManager;  /** 专门用于管理Region Server的管理器  */  ServerManager serverManager;  /** 专门用于管理zk当中nodes的节点  */  AssignmentManager assignmentManager;  /** 负责负载均衡的类  */  private LoadBalancer balancer;  /** 负责读取在hdfs上面的表定义的类 */  private TableDescriptors tableDescriptors;  /** 表级别的分布式锁,专门负责监控模式的变化  */  private TableLockManager tableLockManager;   /** 负责监控表的备份 */  private SnapshotManager snapshotManager;
    View Code

      这些类都会被实例化,具体的顺序就不讲了,这个不是特别重要。开学啦,等到region server过来报道,还要记录一下在zk当中注册了的,但是没有在master这里报道的,不做处理。

    // 等待region server过来注册,至少要有一个启动了    this.serverManager.waitForRegionServers(status);    // 检查在zk当中注册了,但是没在master这里注册的server    for (ServerName sn: this.regionServerTracker.getOnlineServers()) {      if (!this.serverManager.isServerOnline(sn)          && serverManager.checkAlreadySameHostPortAndRecordNewServer(              sn, ServerLoad.EMPTY_SERVERLOAD)) {        LOG.info("Registered server found up in zk but who has not yet "          + "reported in: " + sn);      }    }

    分配META表前的准备工作,Split Meta表的日志

      okay,下面是重头戏了,准备分配meta表了,先启动个计时器。

    // 启动超时检查器了哦if (!masterRecovery) {    this.assignmentManager.startTimeOutMonitor();}

       上代码,从日志文件里面找出来挂了的server,然后对这些server做处理。

      // 从WALs目录下找出挂了的机器    Set
    previouslyFailedServers = this.fileSystemManager .getFailedServersFromLogFolders(); // 删除之前运行的时候正在恢复的region,在zk的recovering-regions下所有的region节点一个不留 this.fileSystemManager.removeStaleRecoveringRegionsFromZK(previouslyFailedServers); // 获取就的meta表的位置,如果在已经挂了的机器上 ServerName oldMetaServerLocation = this.catalogTracker.getMetaLocation(); //如果meta表在之前挂了的server上面,就需要把meta表的日志从日志文件里面单独拿出来 if (oldMetaServerLocation != null && previouslyFailedServers.contains(oldMetaServerLocation)) { splitMetaLogBeforeAssignment(oldMetaServerLocation); }

      F3进入getFailedServersFromLogFolders方法。

         //遍历WALs下面的文件        FileStatus[] logFolders = FSUtils.listStatus(this.fs, logsDirPath, null);//获取在线的server的集合        Set
    onlineServers = ((HMaster) master).getServerManager().getOnlineServers().keySet(); for (FileStatus status : logFolders) { String sn = status.getPath().getName(); //如果目录名里面包含-splitting,就是正在split的日志 if (sn.endsWith(HLog.SPLITTING_EXT)) { sn = sn.substring(0, sn.length() - HLog.SPLITTING_EXT.length()); } //把字符串的机器名转换成ServerName ServerName serverName = ServerName.parseServerName(sn); //如果在线的机器里面不包括这个ServerName就认为它是挂了 if (!onlineServers.contains(serverName)) {
    serverNames.add(serverName); } else { LOG.info("Log folder " + status.getPath() + " belongs to an existing region server"); } }

      从代码上面看得出来,从WALs日志的目录下面找,目录名称里面就包括ServerName,取出来和在线的Server对比一下,把不在线的加到集合里面,最后返回。看来这个目录下都是出了问题的Server才会在这里混。

      我们接着回到上面的逻辑,查出来失败的Server之后,从zk当中把之前的Meta表所在的位置取出来,如果Meta表在这些挂了的Server里面,就糟糕了。。得启动恢复措施了。。。先把META表的日志从日志堆里找出来。我们进入splitMetaLogBeforeAssignment这个方法里面看看吧。

     private void splitMetaLogBeforeAssignment(ServerName currentMetaServer) throws IOException {    //该参数默认为false    if (this.distributedLogReplay) {      // In log replay mode, we mark hbase:meta region as recovering in ZK      Set
    regions = new HashSet
    (); 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来恢复,还有一种是recovered.edit模式,通过创建recovered.edits文件来恢复。文件恢复是通过hbase.master.distributed.log.replay参数来设置,默认是false,走的recovered.edit模式。看得出来,这个函数是为恢复做准备工作的,如果是分布式模式,就执行prepareLogReplay准备日志恢复,否则就开始创建recovered.edits恢复文件。

      (a)prepareLogReplay方法当中,把HRegionInfo.FIRST_META_REGIONINFO这个region添加到了recovering-regions下面,置为恢复中的状态。

      (b)下面看看splitMetaLog吧,它是通过调用这个方法来执行split日志的,通过filter来过滤META或者非META表的日志,META表的日志以.meta结尾。

    public void splitLog(final Set
    serverNames, PathFilter filter) throws IOException { long splitTime = 0, splitLogSize = 0; //修改WALs日志目录的名称,在需要分裂的目录的名称后面加上.splitting,准备分裂 List
    logDirs = getLogDirs(serverNames); //把这些挂了的server记录到splitLogManager的deadWorkers的列表 splitLogManager.handleDeadWorkers(serverNames); splitTime = EnvironmentEdgeManager.currentTimeMillis(); //split日志 splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, filter); splitTime = EnvironmentEdgeManager.currentTimeMillis() - splitTime; //记录split结果到统计数据当中 if (this.metricsMasterFilesystem != null) { if (filter == META_FILTER) { this.metricsMasterFilesystem.addMetaWALSplit(splitTime, splitLogSize); } else { this.metricsMasterFilesystem.addSplit(splitTime, splitLogSize); } } }

      上面也带了不少注释了,不废话了,进splitLogDistributed里面瞅瞅吧。

    public long splitLogDistributed(final Set
    serverNames, final List
    logDirs, PathFilter filter) throws IOException {
    //读取文件 FileStatus[] logfiles = getFileList(logDirs, filter);long totalSize = 0; //任务跟踪器,一个batch包括N个任务,最后统计batch当中的总数 TaskBatch batch = new TaskBatch(); Boolean isMetaRecovery = (filter == null) ? null : false; for (FileStatus lf : logfiles) {
    totalSize += lf.getLen(); //获得root后面的相对路径 String pathToLog = FSUtils.removeRootPath(lf.getPath(), conf); //把任务插入到Split任务列表当中 if (!enqueueSplitTask(pathToLog, batch)) { throw new IOException("duplicate log split scheduled for " + lf.getPath()); } } //等待任务结束 waitForSplittingCompletion(batch, status); if (filter == MasterFileSystem.META_FILTER) isMetaRecovery = true; } //清理recovering的状态,否则region server不让访问正在恢复当中的region this.removeRecoveringRegionsFromZK(serverNames, isMetaRecovery); if (batch.done != batch.installed) { //启动的任务和完成的任务不相等 batch.isDead = true; String msg = "error or interrupted while splitting logs in " + logDirs + " Task = " + batch; throw new IOException(msg); } //最后清理日志 for(Path logDir: logDirs){
    try { if (fs.exists(logDir) && !fs.delete(logDir, false)) {
    } } catch (IOException ioe) {
    } } status.markComplete(msg); return totalSize; }

      它的所有的文件的split文件都插入到一个split队列里面去,然后等待结束,这里面有点儿绕了,它是到zk的splitWALs节点下面为这个文件创建一个节点,不是原来的相对路径名,是URLEncoder.encode(s, "UTF-8")加密过的。

      呵呵,看到这里是不是要晕了呢,它是在zk里面创建一个节点,然后不干活,当然不是啦,在每个Region Server里面读会启动一个SplitLogWorker去负责处理这下面的日志。split处理过程在HLogSplitter.splitLogFile方法里面,具体不讲了,它会把恢复文件在region下面生成一个recovered.edits目录里面。

      

    分配META表

      下面就开始指派Meta表的region啦。

    void assignMeta(MonitoredTask status)      throws InterruptedException, IOException, KeeperException {    // Work on meta region    int assigned = 0;    ServerName logReplayFailedMetaServer = null;    //在RegionStates里面状态状态,表名该region正在变化当中    assignmentManager.getRegionStates().createRegionState(HRegionInfo.FIRST_META_REGIONINFO);    //处理meta表第一个region,重新指派    boolean rit = this.assignmentManager.processRegionInTransitionAndBlockUntilAssigned(HRegionInfo.FIRST_META_REGIONINFO);    //这个应该是meta表,hbase:meta,等待它在zk当中可以被访问    boolean metaRegionLocation = this.catalogTracker.verifyMetaRegionLocation(timeout);    if (!metaRegionLocation) {
    assigned++; if (!rit) { // 没分配成功,又得回头再做一遍准备工作 ServerName currentMetaServer = this.catalogTracker.getMetaLocation(); if (currentMetaServer != null) { if (expireIfOnline(currentMetaServer)) { splitMetaLogBeforeAssignment(currentMetaServer); if (this.distributedLogReplay) { logReplayFailedMetaServer = currentMetaServer; } } } //删掉zk当中的meta表的位置,再分配 assignmentManager.assignMeta(); } } else { //指派了,就更新一下它的状态为online this.assignmentManager.regionOnline(HRegionInfo.FIRST_META_REGIONINFO,this.catalogTracker.getMetaLocation()); } //在zk当中启用meta表 enableMeta(TableName.META_TABLE_NAME); // 启动关机处理线程 enableServerShutdownHandler(assigned != 0); if (logReplayFailedMetaServer != null) { // 这里不是再来一次,注意了啊,这个是分布式模式状态下要进行的一次meta表的日志split, //回头看一下这个变量啥时候赋值就知道了 this.fileSystemManager.splitMetaLog(logReplayFailedMetaServer); } }

      历经千辛万苦跟踪到了这个方法里面,通过RPC,向随机抽出来的Region Server发送请求,让它打开region。

    public RegionOpeningState sendRegionOpen(final ServerName server,      HRegionInfo region, int versionOfOfflineNode, List
    favoredNodes) throws IOException { AdminService.BlockingInterface admin = getRsAdmin(server); if (admin == null) {
    return RegionOpeningState.FAILED_OPENING; } //构建openRegion请求, OpenRegionRequest request = RequestConverter.buildOpenRegionRequest(region, versionOfOfflineNode, favoredNodes); try { //调用指定的Region Server的openRegion方法 OpenRegionResponse response = admin.openRegion(null, request); return ResponseConverter.getRegionOpeningState(response); } catch (ServiceException se) { throw ProtobufUtil.getRemoteException(se); } }

      这个工作完成, 如果是分布式文件恢复模式,还需要进行这个工作,recovered.edit模式之前已经干过了

    //获取正在恢复的meta region serverSet
    previouslyFailedMetaRSs = getPreviouselyFailedMetaServersFromZK();if (this.distributedLogReplay && (!previouslyFailedMetaRSs.isEmpty())) { previouslyFailedMetaRSs.addAll(previouslyFailedServers); this.fileSystemManager.splitMetaLog(previouslyFailedMetaRSs);}

      

    分配用户Region

      之后就是一些清理工作了,处理掉失败的server,修改一些不正确的region的状态,分配所有用户的region。

    // 已经恢复了meta表,我们现在要处理掉其它失败的server    for (ServerName tmpServer : previouslyFailedServers) {      this.serverManager.processDeadServer(tmpServer, true);    }    // 如果是failover的情况,就修复assignmentManager当中有问题的region状态,如果是新启动的,就分配所有的用户region    this.assignmentManager.joinCluster();

      

      分配region的工作都是由assignmentManager来完成的,在joinCluster方法中的调用的processDeadServersAndRegionsInTransition的最后一句调用的assignAllUserRegions方法,隐藏得很深。。经过分配过的region,hmaster在启动的时候默认会沿用上一次的结果,就不再变动了,这个是由一个参数来维护的hbase.master.startup.retainassign,默认是true。分配用户region的方法和分配meta表的过程基本是一致的。

      至此HMaster的启动过程做的工作基本结束了。

     

     

    转载于:https://www.cnblogs.com/cenyuhai/p/3708335.html

    你可能感兴趣的文章
    微信小程序模板介绍
    查看>>
    JDK源码阅读-Iterable接口
    查看>>
    【算法刷题】2:寻找两个有序数组的中位数
    查看>>
    Linux学习笔记(三)-Linux 磁盘与文件系统管理
    查看>>
    站在巨人的肩膀上
    查看>>
    阿里巴巴fastjson @JSONField 注解说明
    查看>>
    Android开发 - 解决DialogFragment在全屏时View被状态栏遮住的问题
    查看>>
    行为型模式:模板方法
    查看>>
    Linux命令
    查看>>
    5、Flutter Widget - AbsorbPointer;
    查看>>
    游戏安全资讯精选 2018年第三期:中国已经诞生了区块链手游,游戏公司向直播答题行业的借鉴宝典,微软“周二补丁日”一览...
    查看>>
    第一章:SpringCloud服务发现和服务注册
    查看>>
    区块链:定义未来金融与经济新格局
    查看>>
    Dom事件
    查看>>
    小册笔记
    查看>>
    mongoDB高级查询这一篇就够了
    查看>>
    js节流和防抖
    查看>>
    MySQL学习笔记之三排序和过滤
    查看>>
    VUE 使用笔记
    查看>>
    (转)Android studio 多渠道打包(超简洁版)
    查看>>