HBase 源码阅读(一)

1. HMaster main方法

在上文中Macos M1 IDEA本地调试 HBase 2.2.2,我们使用HMaster的主函数 使用"start"作为入参,启动了HMaster进程

这里我们再深入了解下HMaster的运行机理

java 复制代码
  public static void main(String [] args) {
    LOG.info("STARTING service " + HMaster.class.getSimpleName());
    VersionInfo.logVersion();
    new HMasterCommandLine(HMaster.class).doMain(args);

  }

这里构建了HMasterCommandLine 一个新的对象, 调用了HMasterCommandLine的doMain方法

java 复制代码
public class HMasterCommandLine extends ServerCommandLine 

HMasterCommandLine 继承 ServerCommandLine方法,用于处理命令行参数,并启动相应的服务

ServerCommandLine 类中的doMain方法,HMaster main方法中的doMain(args)通过这个内容进行切分

java 复制代码
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);
  }
}

这里会通过ToolRunner.run调用 HMasterCommandLine的run方法(因为是HMasterCommandLine继承了这个类,这里的this 指的就是HMasterCommandLine 类的内容)

这里还会载入HBase的配置文件

java 复制代码
HBaseConfiguration.create()

这里就会创建一个HMasterCommandLine实例,并调用它的run方法

java 复制代码
 new HMasterCommandLine(HMaster.class).doMain(args);

一个常见的模式是通过反射调用传递的类(在这里是HMaster.class)的构造器,并在构造器完成后调用相应的run()start()方法。由于HMasterCommandLine是带有HMaster类信息的,所以doMain最终会创建一个HMaster实例,这就会调用HMaster的构造函数。

2. HMasterCommandLine的run 方法

载入Options 信息

判断需要创建多少master进程,多少RegionServers进程

最后判断用户输入的命令行数据,这里会调用HMasterCommandLine的startMaster 方法

java 复制代码
    if ("start".equals(command)) {
      System.out.println("调用启动函数");
      return startMaster();
    } else if ("stop".equals(command)) {
      return stopMaster();
    } else if ("clear".equals(command)) {
      return (ZNodeClearer.clear(getConf()) ? 0 : 1);
    } else {
      usage("Invalid command: " + command);
      return 1;
    }

3. HMasterCommandLine的startMaster方法

获取配置文件

这里我们设置的是在一台JVM下同时启动master和regionserver,否则只会在本JVM上创建一个Master进程

由于是Mini 集群 这里也会相应的创建一个MiniZookeeperCluster对象

同时当 master 关闭时,需要关闭 zk 集群。

运行一个子类,在退出时关闭 zk 集群。

java 复制代码
// 这一行代码,就会调用HMaster的构造方法
        LocalHBaseCluster cluster = new LocalHBaseCluster(conf, mastersCount, regionServersCount,
          LocalHMaster.class, HRegionServer.class);


        ((LocalHMaster)cluster.getMaster(0)).setZKCluster(zooKeeperCluster);
        cluster.startup();
        waitOnMasterThreads(cluster);

这里在本地创建了HBaseCluster内容,设置了master数目,Regionserver数目,LocalHMaster类(继承了HMaster),在后面设置了Mini Zookeeper 的 对象,此时,如果关闭cluster,也会同时关闭zooKeeperCluster

LocalHBaseCluster 的构造方法中,它会根据传入的 LocalHMaster.classHRegionServer.class 创建相应的 HMasterHRegionServer 实例(即调用他们的构造器)

最后执行

复制代码
cluster.startup();

由JVMClusterUtil 启动各自的线程(即HMaster的run方法)

java 复制代码
  public void startup() throws IOException {
    JVMClusterUtil.startup(this.masterThreads, this.regionThreads);
  }

4. HMaster

复制代码
public class HMaster extends HRegionServer implements MasterServices

HMaster 继承HRegionServer类,实现了MasterServices 接口

HRegionServer为客户端提供一组 HRegions。它与HMaster 进行检查。单个 HBase 部署中有许多 HRegionServer

而MasterServices 为 HMaster 提供的精选服务子集。仅供内部使用。传递给Managers,Services and Chores,因此在测试时可以传递非完整的 HMaster。

5. HMaster Constructor

HMaster Constructor,过程

  1. 初始化本地HRegionServer

  2. 启动 ActiveMasterManager

    在Master变为Active状态后,剩余的初始化步骤在finishActiveMasterInitialization(MonitoredTask) 方法下进行

初始化本地HRegionServer 是通过 super(conf)执行的

复制代码
  public HMaster(final Configuration conf)
      throws IOException, KeeperException {
    // 初始化本地HRegionServer
    super(conf);

6. HRegionServer.java类 继承了HasThread 类

HasThread类

包含线程并将常用线程方法委托给该实例的抽象类。此类的目的是解决 Sun JVM 错误 #6915621,其中 JDK 内部的某些内容使用 Thread.currentThread() 作为监视器锁。这会产生类似 HBASE-4367、HBASE-4101 等死锁

7. 这里有一个经常出现的类 TraceUtil

这个包装类提供了以简化的方式访问 htrace 4+ 功能的函数。

具体可以参考

https://www.cnblogs.com/itsoku123/p/11377738.html

初始化了HBaseHTraceConfiguration的一个对象

8. 回到HRegionServer的 Constructor

初始化了一些HRegionServer的参数

创建Rpc服务

复制代码
      rpcServices = createRpcServices();

调用

复制代码
getUseThisHostnameInstead(conf)

HMaster 应该重写此方法来加载 master 的特定配置

构建ServerName

构建

复制代码
rpcControllerFactory
rpcRetryingCallerFactory

如果安全性zookeeper, 安全hadoop等 需要登录

复制代码
      // login the zookeeper client principal (if using security)
      ZKUtil.loginClient(this.conf, HConstants.ZK_CLIENT_KEYTAB_FILE,
          HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL, hostName);
      // login the server principal (if using secure Hadoop)
      login(userProvider, hostName);
      // init superusers and add the server principal (if using security)
      // or process owner as default super user.
      Superusers.initialize(conf);
      regionServerAccounting = new RegionServerAccounting(conf);

检测Master是否带表,如果不带表,说明是一个新的Master,不需要实例化块缓存和 mob 文件缓存

复制代码
initializeFileSystem()

# 如果系统运行在Windows上, 则进行一个windows特殊化设置
setupWindows(getConfiguration(), getConfigurationManager());

下面是关于一部分启动Zookeeper 的内容

9. HRegionServer initializeFileSystem()函数

这里Constructor 使用这个函数构建了HBase底层的文件存储层

java 复制代码
private void initializeFileSystem() throws IOException {
// 获取此 RS(HRegionServer) 使用的 fs 实例。我们是否在 hbase 中使用校验和验证?如果 hbase
// 启用了校验和验证,则自动关闭 hdfs 校验和验证。

boolean useHBaseChecksum = conf.getBoolean(HConstants.HBASE_CHECKSUM_VERIFICATION, true);

FSUtils.setFsDefault(this.conf, FSUtils.getWALRootDir(this.conf));
this.walFs = new HFileSystem(this.conf, useHBaseChecksum);
this.walRootDir = FSUtils.getWALRootDir(this.conf);

// 这里的fs.defaultFS时通过hbase-site.xml设置的
// 设置"fs.defaultFS"以匹配 hbase.rootdir 上的文件系统,否则
// 底层 hadoop hdfs 访问器将与错误的文件系统相冲突
// (除非全部设置为默认值)。
FSUtils.setFsDefault(this.conf,FSUtils.getRootDir(this.conf));
this.fs = new HFileSystem(this.conf,useHBaseChecksum);
this.rootDir = FSUtils.getRootDir(this.conf);
this.tableDescriptors = getFsTableDescriptors();
}

获取了HDFS底层存储的HBase 表结构

复制代码
this.tableDescriptors = getFsTableDescriptors();

10. 回到HMaster的构建器中

此时,HMaster的第一个步骤已经完成, 下一步是要启动 ActiveMasterManager

在HMaster的Constructor中,并没有找到finishActiveMasterInitialization的入口,我们直接找到HMaster.java文件中finishActiveMasterInitialization函数,往上找该方法的调用

finishActiveMasterInitialization先是被 startActiveMasterManage方法调用, startActiveMasterManage又被run()方法调用,

在Java中,run()方法并不会在构造函数之后自动执行。通常情况下,run()方法是与Thread类或实现Runnable接口的类关联的。

关于Runnable 类的介绍

Java并发编程(二):Thread与Runnable的底层原理

11. HMaster的run方法

判断是否在本地测试时启动一个完整的HBase集群

此时,通过Threads类,启动了一个线程

调用了startActiveMasterManager 函数

最后执行了

复制代码
 super.run();

此时,调用HRegionServer的run方法

12. HMaster的startActiveMasterManager方法

java 复制代码
   // 构建了一个Znode路径,用于表示当前HMaster 作为主节点的Znode,
    // ZNodePaths.joinZNode方法将ZooKeeper中备份主节点目录的路径和当前HMaster的serverName拼接成一个完整的ZNode路径。
    String backupZNode = ZNodePaths.joinZNode(
      zooKeeper.getZNodePaths().backupMasterAddressesZNode, serverName.toString());
    /*
    * Add a ZNode for ourselves in the backup master directory since we
    * may not become the active master. If so, we want the actual active
    * master to know we are backup masters, so that it won't assign
    * regions to us if so configured.
    *
    * If we become the active master later, ActiveMasterManager will delete
    * this node explicitly.  If we crash before then, ZooKeeper will delete
    * this node for us since it is ephemeral.
    */
    LOG.info("Adding backup master ZNode " + backupZNode);
    // 创建一个临时的Znode,setMasterAddress将当前HMaster的信息写入到指定的Znode中
    if (!MasterAddressTracker.setMasterAddress(zooKeeper, backupZNode, serverName, infoPort)) {
      LOG.warn("Failed create of " + backupZNode + " by " + serverName);
    }
    // 设置信息端口
    this.activeMasterManager.setInfoPort(infoPort);
    int timeout = conf.getInt(HConstants.ZK_SESSION_TIMEOUT, HConstants.DEFAULT_ZK_SESSION_TIMEOUT);
    // If we're a backup master, stall until a primary to write this address

    // 判断是否是备份模式,如果是备份模式,则等到又一个active的主节点在Zookeeper中注册
    // 这中等待通过轮训的方式实现,直到activeMasterManager 有一个活动的主节点
    if (conf.getBoolean(HConstants.MASTER_TYPE_BACKUP, HConstants.DEFAULT_MASTER_TYPE_BACKUP)) {
      LOG.debug("HMaster started in backup mode. Stalling until master znode is written.");
      // This will only be a minute or so while the cluster starts up,
      // so don't worry about setting watches on the parent znode
      while (!activeMasterManager.hasActiveMaster()) {
        LOG.debug("Waiting for master address and cluster state znode to be written.");
        Threads.sleep(timeout);
      }
    }
    // 创建任务监控状态
    MonitoredTask status = TaskMonitor.get().createStatus("Master startup");
    status.setDescription("Master startup");
    try {
      // 尝试成为Active 主节点
      if (activeMasterManager.blockUntilBecomingActiveMaster(timeout, status)) {
        finishActiveMasterInitialization(status);
      }
      // 下面就是错误处理

13. HMaster 的 finishActiveMasterInitialzation方法

代码中的介绍,HMaster构建的最主要的部分(295行代码)

成为主 master 后完成 HMaster 的初始化。启动顺序有点复杂但非常重要,除非您知道自己在做什么,否则请不要更改它。

  1. 初始化基于文件系统的组件 - 文件系统管理器、wal 管理器、表描述符等

  2. 发布集群 ID

  3. 这是最复杂的部分 - 初始化服务器管理器、分配管理器和区域服务器跟踪器

    1. 创建服务器管理器

    2. 创建过程执行器,加载过程,但不要启动工作器。我们将在完成 SCP 调度后稍后启动它,以避免为同一服务器调度重复的 SCP

    3. 创建分配管理器并启动它,加载元区域状态,但不要从元区域加载数据

    4. 启动区域服务器跟踪器,构建在线服务器集并找出死机服务器并为它们调度 SCP。在线服务器将通过扫描 zk 来构建,我们还将扫描 wal 目录以找出可能的活动区域服务器,这两组之间的差异就是死服务器

  4. 如果这是新部署,请安排 InitMetaProcedure 来初始化元数据

  5. 启动必要的服务线程 - 平衡器、目录管理员、执行器服务以及过程执行器等。请注意,必须首先创建平衡器,因为分配管理器在分配区域时可能会使用它。

  6. 等待元数据初始化(如有必要),启动表状态管理器。

  7. 等待足够的区域服务器签入

  8. 让分配管理器从元数据加载数据并构建区域状态

  9. 启动所有其他事情,例如杂务服务等

    请注意,现在我们不会安排特殊程序使元数据在线(除非第一次尚未创建元数据),我们将依靠 SCP 使元数据在线。

13.1 根据Master内容,预先载入内存数据

在初始化文件系统组件之前,会根据master是否带表,初始化MemStoreLAB

这里对应的应该是LSM-Tree里的Memory层

java 复制代码
  protected void initializeMemStoreChunkCreator() {
    // 检查是否启用了MemStoreLAB
    if (MemStoreLAB.isEnabled(conf)) {
      // MSLAB is enabled. So initialize MemStoreChunkPool
      // By this time, the MemstoreFlusher is already initialized. We can get the global limits from
      // it.
      // 获取全局MemStore大小吗这是HBase 用来存储到内存中的大小限制,此值用于计算内存块池的大小
      Pair<Long, MemoryType> pair = MemorySizeUtil.getGlobalMemStoreSize(conf);
      long globalMemStoreSize = pair.getFirst();

      // 检查是否使用OffHeap内存,这里的Offheap指的是直接内存,非堆内存,如果启用,内存管理将不同于常规的内存管理
      boolean offheap = this.regionServerAccounting.isOffheap();
      // When off heap memstore in use, take full area for chunk pool.

      // 确定内存池大小的百分比
      // 如果使用OffHeap内存,则内存池大小将设为100%,饿否则,将从配置中获取MemStoreLAB内存池大小的最大百分比(默认0.4
      float poolSizePercentage = offheap? 1.0F:
          conf.getFloat(MemStoreLAB.CHUNK_POOL_MAXSIZE_KEY, MemStoreLAB.POOL_MAX_SIZE_DEFAULT);
      float initialCountPercentage = conf.getFloat(MemStoreLAB.CHUNK_POOL_INITIALSIZE_KEY,
          MemStoreLAB.POOL_INITIAL_SIZE_DEFAULT);

      // 获取内存块儿大小
      int chunkSize = conf.getInt(MemStoreLAB.CHUNK_SIZE_KEY, MemStoreLAB.CHUNK_SIZE_DEFAULT);
      // init the chunkCreator
      //初始化ChunkCreator
      // chunkSize : 每个内存块大小
      // offheap : 是否使用Offheap内存
      // globalMemStoreSize  // 全局MemStore的内存限制
      // poolSizePercentage // 内存池的最大大小百分比
      // initialCountPercentage // 初始内存池的大小把粉笔
      // hMemManager:  // 管理内存回收的管理器

      ChunkCreator.initialize(chunkSize, offheap, globalMemStoreSize, poolSizePercentage,
        initialCountPercentage, this.hMemManager);
    }

13. 2 初始化文件系统组件

java 复制代码
    
    this.fileSystemManager = new MasterFileSystem(conf);
    this.walManager = new MasterWalManager(this);

    // enable table descriptors cache
    this.tableDescriptors.setCacheOn();

    // warm-up HTDs cache on master initialization
    if (preLoadTableDescriptors) {
      status.setStatus("Pre-loading table descriptors");
      this.tableDescriptors.getAll();
    }

13.3 发布CLuster ID

发布集群 ID;也在 Master 中设置它。超类 RegionServer 稍后会执行此操作,但

仅在它与 Master 签入后。至少有几个测试在 Master 调用其运行方法之前和 RegionServer 完成 reportForDuty 之前向 Master 询问 clusterId。

预防措施。设置旧的 hbck1 锁定文件,以隔离运行其

hbck1 的旧 hbase1 对抗 hbase2 集群;这可能会造成损害。要跳过此行为,请将

hbase.write.hbck1.lock.file 设置为 false。

复制代码
    ClusterId clusterId = fileSystemManager.getClusterId();
    status.setStatus("Publishing Cluster ID " + clusterId + " in ZooKeeper");
    ZKClusterId.setClusterId(this.zooKeeper, fileSystemManager.getClusterId());
    this.clusterId = clusterId.toString();

    if (this.conf.getBoolean("hbase.write.hbck1.lock.file", true)) {
      HBaseFsck.checkAndMarkRunningHbck(this.conf,
          HBaseFsck.createLockRetryCounterFactory(this.conf).create());
    }

后面内容比较多,我就不一一列举了,可以通过IDEA点击函数名来进行学习

14 Super.run()启动一个HRegionServer 的run方法

相关推荐
延凡科技24 分钟前
多场景落地复盘:端边云架构无人机智能巡检系统设计与实践
大数据·数据结构·人工智能·科技·架构·无人机·能源
ifenxi爱分析1 小时前
爱分析最新报告解读:AI数据基础设施与数据中台的区别
大数据·人工智能
oradh2 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
品牌全球行2 小时前
共商共建共享 链接数字未来——“一带一路数字新城(深圳)会客厅筹备办”揭牌仪式在深圳隆重举行
大数据·人工智能
z123456789863 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD3 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward4 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记4 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发4 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB4 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库