HBase 源码分析 -- 创建Table表

HBase的代码是基于1.2.6的

1. 客户端

客户端的代码如下:

public class CreateTable {
	public static void main(String[] args) throws IOException, ServiceException {
		// new一个配置对象
		Configuration conf = HBaseConfiguration.create();
		Connection conn = ConnectionFactory.createConnection(conf);
		HBaseAdmin.checkHBaseAvailable(conf);
		Admin admin = conn.getAdmin();

		// 初始化表描述
		HTableDescriptor tableDescriptor = new HTableDescriptor(
				TableName.valueOf("student"));

		// 给表描述对象增加列族
		tableDescriptor.addFamily(new HColumnDescriptor("name"));
		tableDescriptor.addFamily(new HColumnDescriptor("age"));

		// 让admin根据tableDescriptor执行建表操作
		admin.createTable(tableDescriptor);
		System.out.println("hbase表创建成功! 表名为emp,列族有personal , professional");
	}
}

2. 服务端

客户端执行代码createTable()之后,这个消息会通过RPC的方式,调用HMaster::createTable()函数。其整体流程中如下:

客户端通过RPC调用HMaster的createTable()函数。在这个函数中,

 public long createTable() throws IOException {
    if (isStopped()) {
      throw new MasterNotRunningException();
    }

    String namespace = hTableDescriptor.getTableName().getNamespaceAsString();
    ensureNamespaceExists(namespace);

    final HRegionInfo[] newRegions =
        ModifyRegionUtils.createHRegionInfos(hTableDescriptor, splitKeys);
    checkInitialized();
    sanityCheckTableDescriptor(hTableDescriptor);

    return MasterProcedureUtil.submitProcedure(
      new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
      @Override
      protected void run() throws IOException {
        getMaster().getMasterCoprocessorHost().preCreateTable(hTableDescriptor, newRegions);

        LOG.info(getClientIdAuditPrefix() + " create " + hTableDescriptor);

        // TODO: We can handle/merge duplicate requests, and differentiate the case of
        //       TableExistsException by saying if the schema is the same or not.
        ProcedurePrepareLatch latch = ProcedurePrepareLatch.createLatch();
        submitProcedure(new CreateTableProcedure(
          procedureExecutor.getEnvironment(), hTableDescriptor, newRegions, latch));
        latch.await();

        getMaster().getMasterCoprocessorHost().postCreateTable(hTableDescriptor, newRegions);
      }

      @Override
      protected String getDescription() {
        return "CreateTableProcedure";
      }
    });
  }

其整体流程图如下:
在这里插入图片描述

1)客户端的请求,会调用HMaster::CreateTable函数,在这个函数会会创建一个CreateTablePorcedure对象(Procedure的实现), 并调用submitPorcedure

2)在submitProcedure中,会将这个Procedure放入到一个Set集 – runnables中。

3) HMaster的ProcedureExecuteor会调动一个线程,这个线程执行函数execLoop(),这个函数会不断扫描runnable这个对象,如果有数据,就会从中取出来,然后调用函数execProcedure()去处理这个Procedure

4)而execProcedure,最终会调用CreateTableProcedure::executeFromState()函数去处理。

5)当一切处理完成姤 ,就执行退出。显示创建table成功.

2. CreateTableProcedure的executeFromState过程

上面已经说过了,创建表的过程的实际动作是在executeFromState中完成的。
而此函数的流程图如下:
在这里插入图片描述

这里选择其中的几个重要步骤进行说明。

2.1 创建Region的过程

创建表时,需要先创建Region,然后再部署到相应的RegionServer上去,这是两部分。其中创建Region是在CREATE_TABLE_WRITE_FS_LAYOUT中实现的。
其对应的函数是CreateTableProcedure::createFsLayout(),其代码如下:

  protected static List<HRegionInfo> createFsLayout(...) throws IOException {
    final MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
    final Path tempdir = mfs.getTempDir();

    // 1. Create Table Descriptor
    // using a copy of descriptor, table will be created enabling first
    final Path tempTableDir = FSUtils.getTableDir(tempdir, hTableDescriptor.getTableName());
    new FSTableDescriptors(env.getMasterConfiguration()).createTableDescriptorForTableDirectory(
      tempTableDir, hTableDescriptor, false);

    // 2. Create Regions
    newRegions = hdfsRegionHandler.createHdfsRegions(env, tempdir,
      hTableDescriptor.getTableName(), newRegions);

    // 3. Move Table temp directory to the hbase root location 创建相应的HDFS目录
      ... ...
    return newRegions;
  }

从上面注释可以看到,这一过程分为三个部分:创建Table的Descriptor,创建Region信息, 最后创建相应的HDFS目录

2.2 部署Region的过程

创建完了Region之后,就需要将这个Region在实际的RegionServer上面部署起来。它会调用

protected static void assignRegions(final MasterProcedureEnv env,
      final TableName tableName, final List<HRegionInfo> regions)
      throws HBaseException, IOException {
    ProcedureSyncWait.waitRegionServers(env);
    
    final AssignmentManager assignmentManager = env.getMasterServices().getAssignmentManager();

    // Mark the table as Enabling 设置zookeeper中的状态信息
    assignmentManager.getTableStateManager().setTableState(tableName,
        ZooKeeperProtos.Table.State.ENABLING);

    // Trigger immediate assignment of the regions in round-robin fashion
    // 部署Region
    ModifyRegionUtils.assignRegions(assignmentManager, regions);

    // Enable table
    assignmentManager.getTableStateManager()
      .setTableState(tableName, ZooKeeperProtos.Table.State.ENABLED);
  }

再看一下assignRegions的实现。

下面是AssignRegions中的调用栈过程,其中sendRegionOpen()就是发送到RegionServer上去执行。

	org.apache.hadoop.hbase.master.ServerManager	ServerManager.java:sendRegionOpen 800
	org.apache.hadoop.hbase.master.AssignmentManager	AssignmentManager.java:assign 1739
	org.apache.hadoop.hbase.master.AssignmentManager	AssignmentManager.java:assign 2851
	org.apache.hadoop.hbase.master.AssignmentManager	AssignmentManager.java:assign 2830
	org.apache.hadoop.hbase.util.ModifyRegionUtils	ModifyRegionUtils.java:assignRegions 287
	org.apache.hadoop.hbase.master.procedure.CreateTableProcedure	CreateTableProcedure.java:assignRegions 452
	org.apache.hadoop.hbase.master.procedure.CreateTableProcedure	CreateTableProcedure.java:executeFromState 127
	org.apache.hadoop.hbase.master.procedure.CreateTableProcedure	CreateTableProcedure.java:executeFromState 58
	org.apache.hadoop.hbase.procedure2.StateMachineProcedure	StateMachineProcedure.java:execute 119
	org.apache.hadoop.hbase.procedure2.Procedure	Procedure.java:doExecute 498
	org.apache.hadoop.hbase.procedure2.ProcedureExecutor	ProcedureExecutor.java:execProcedure 1152
	org.apache.hadoop.hbase.procedure2.ProcedureExecutor	ProcedureExecutor.java:execLoop 947
	org.apache.hadoop.hbase.procedure2.ProcedureExecutor	ProcedureExecutor.java:execLoop 900
	org.apache.hadoop.hbase.procedure2.ProcedureExecutor	ProcedureExecutor.java:access$400 77
	org.apache.hadoop.hbase.procedure2.ProcedureExecutor$2	ProcedureExecutor.java:run 497

具体看一下assign中过程。这里有两个过程,需要关注:Master是如何选择RegionServer,以及Region如何部署到RegionServer上去的。

2.2.1 RegionServer的选择

查看AssignmentManager::assign()的代码可看到:

public void assign(List<HRegionInfo> regions)
      throws IOException, InterruptedException {
      ... 
  //首先找出现在属于online状态的regionserver,将这些regionserver放到list列表中
  List<ServerName> servers = serverManager.createDestinationServersList();
      ... 
  // Generate a round-robin bulk assignment plan
  //默认的情况下,是调用BaseLoadBalancer::roundRobinAssignment()函数
  Map<ServerName, List<HRegionInfo>> bulkPlan = balancer.roundRobinAssignment()(regions, servers);

  ... 
  processFavoredNodes(regions);
  assign(regions.size(), servers.size(), "round-robin=true", bulkPlan);
}

进一步分析函数BaseLoadBalancer::roundRobinAssignment()

private void roundRobinAssignment(...) {

		int numServers = servers.size();
		int numRegions = regions.size();
		//设置regions数与numServers的比例,即一个Server上面有多少个regions
		int max = (int) Math.ceil((float) numRegions / numServers);
		int serverIdx = 0;

		for (int j = 0; j < numServers; j++) {
			//按server进行分配regions
			ServerName server = servers.get((j + serverIdx) % numServers);
			List<HRegionInfo> serverRegions = new ArrayList<HRegionInfo>(max);
			//需要注意的是这里的i的步进是server的个数。让region分散在各个server上面
			for (int i = regionIdx; i < numRegions; i += numServers) {
				//取出一个region
				HRegionInfo region = regions.get(i % numRegions);
					。。。 。。。 
					serverRegions.add(region);
					cluster.doAssignRegion(region, server);
				}
			}
			//将分配好的region与server的关系放到assignments的map中
			assignments.put(server, serverRegions);
			regionIdx++;
		}
	}

3. 总结

HBase创建表的时候,分为多个步骤,通过不同步骤的状态量进行程序的执行。所有的动作也是在CreateTableProcedure::executeFromState() 完成的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值