hbase 源代码分析(5)regionLocator 获取region过程 详解

按计划今天是分析Hbase GET的过程。

上一篇:hbase 源代码解析(4) 的createTable 的 region assign
http://blog.csdn.net/chenfenggang/article/details/75000230

在我以前的印象中,get过程应该是
client 找zk 拿到HMaster的主机地址,然后和HMaster 由HMaster 找regionService。拿到region。region再找menStore或者StoreFile。在到HDFS 过程。

结果再今天走读代码的时候,感觉一下子就从client 到RegionService。中间流程总是不对,后来细心分析,才发现流程是这样的

client 找 zk 拿到META 表的regionService主机。然后直接访问这台机器, 在 META regionService 表中找的table的行记录的region 的主机。然后再次发送客户端请求。在这个里面找到数据。

而前面的过程其实是 regionLocator 的过程。反正这个是我后的一个点。所以先写这个过程。

     RegionLocator regionLocator = connection.getRegionLocator(TableName.META_TABLE_NAME);
            regionLocator.getRegionLocation("rowKey".getBytes());

regionLocator 里很简单。简单到里面就放了两个东西。connection和table。通过connection可以连接世界,通过table可以在世界定位于你。里面最重要的方法getRegionLocation。通过传入参数rowkey。可以重世界上获得table的当前rowkey的region所有信息。

这里可以获取多个版本的所有信息,但是默认只获取一个就够了。之后的过程会去zk里确认一下表是否enable状态。

   @Override
    public HRegionLocation relocateRegion(final TableName tableName,
        final byte [] row) throws IOException{
      RegionLocations locations =  relocateRegion(tableName, row,
        RegionReplicaUtil.DEFAULT_REPLICA_ID);
      return locations == null ? null :
        locations.getRegionLocation(RegionReplicaUtil.DEFAULT_REPLICA_ID);
    }

然后在这里做了一个分支:如果是META表,走locateMeta 否则走locateRegionInMeta 现在是获取非META表。之后会去缓存里查找一次是否有信息。如果没就继续
if (tableName.equals(TableName.META_TABLE_NAME)) {
return locateMeta(tableName, useCache, replicaId);
} else {
// Region not in the cache - have to go to the meta RS
return locateRegionInMeta(tableName, row, useCache, retry, replicaId);
}

在这里的时候调用了这个

  /*
      * Search the hbase:meta table for the HRegionLocation
      * info that contains the table and row we're seeking.
      */
    private RegionLocations locateRegionInMeta(TableName tableName, byte[] row,
                   boolean useCache, boolean retry, int replicaId) throws IOException {
         ..............
            rcs = new ClientSmallReversedScanner(conf, s, TableName.META_TABLE_NAME, this,
              rpcCallerFactory, rpcControllerFactory, getMetaLookupPool(), 0);

            regionInfoRow = rcs.next();
          .............
          // convert the row result into the HRegionLocation we need!
          RegionLocations locations = MetaTableAccessor.getRegionLocations(regionInfoRow);
            .........
          ServerName serverName = locations.getRegionLocation(replicaId).getServerName();
          if (serverName == null) {
            throw new NoServerForRegionException("No server address listed " +
              "in " + TableName.META_TABLE_NAME + " for region " +
              regionInfo.getRegionNameAsString() + " containing row " +
              Bytes.toStringBinary(row));
          ........................................
    }

这里是拿到serverName的最主要的地方。 rcs.next()最重要。

这个面有个方法

  Result[] call(ScannerCallableWithReplicas callable,
      RpcRetryingCaller<Result[]> caller, int scannerTimeout)
      throws IOException, RuntimeException {
    if (Thread.interrupted()) {
      throw new InterruptedIOException();
    }
    // callWithoutRetries is at this layer. Within the ScannerCallableWithReplicas,
    // we do a callWithRetries
    return caller.callWithoutRetries(callable, scannerTimeout);
  }

然后: callable.call(scannerTimeout)
所以这里最重要的是找到callable。回到new 对象过程

 rcs = new ClientSmallReversedScanner(conf, s, TableName.META_TABLE_NAME, this,
              rpcCallerFactory, rpcControllerFactory, getMetaLookupPool(), 0);

的时候

 protected boolean nextScanner(int nbRows, final boolean done) throws IOException {
   .......
    try {
      callable = getScannerCallable(localStartKey, nbRows);
      // Open a scanner on the region server starting at the
      // beginning of the region
      call(callable, caller, scannerTimeout);
      this.currentRegion = callable.getHRegionInfo();
      .....
     }
  }

这个里面又是一个callable。那么这个callable是什么呢。

 @InterfaceAudience.Private
    protected ScannerCallableWithReplicas getScannerCallable(byte [] localStartKey,
        int nbRows) {
      scan.setStartRow(localStartKey);
      ScannerCallable s =
          new ScannerCallable(getConnection(), getTable(), scan, this.scanMetrics,
              this.rpcControllerFactory);
      s.setCaching(nbRows);
      ScannerCallableWithReplicas sr = new ScannerCallableWithReplicas(tableName, getConnection(),
       s, pool, primaryOperationTimeout, scan,
       retries, scannerTimeout, caching, conf, caller);
      return sr;
    }

有点绕,但是这个还么结束,因为我们还是不知道这个callable的ServiceName是什么,因为每一次call需要一个stub ,而stub需要指定主机名。
还好,还好在caller调用的时候有

   callable.prepare(false);
      return callable.call(callTimeout);

这个prepare 很重要,上一个prepare我去看了是空的,所以好几次默认这个也是空的。但是最后还是被我发现了

这个prepare很有趣所以代码全部拷贝

 @Override
  public void prepare(boolean reload) throws IOException {
    if (Thread.interrupted()) {
      throw new InterruptedIOException();
    }
    RegionLocations rl = RpcRetryingCallerWithReadReplicas.getRegionLocations(!reload,
        id, getConnection(), getTableName(), getRow());
    location = id < rl.size() ? rl.getRegionLocation(id) : null;
    if (location == null || location.getServerName() == null) {
      // With this exception, there will be a retry. The location can be null for a replica
      //  when the table is created or after a split.
      throw new HBaseIOException("There is no location for replica id #" + id);
    }
    ServerName dest = location.getServerName();
    setStub(super.getConnection().getClient(dest));
    if (!instantiated || reload) {
      checkIfRegionServerIsRemote();
      instantiated = true;
    }
    // check how often we retry.
    // HConnectionManager will call instantiateServer with reload==true
    // if and only if for retries.
    if (reload && this.scanMetrics != null) {
      this.scanMetrics.countOfRPCRetries.incrementAndGet();
      if (isRegionServerRemote) {
        this.scanMetrics.countOfRemoteRPCRetries.incrementAndGet();
      }
    }
  }

这个里面调用了获取getRegionLocations方法。

RegionLocations rl = RpcRetryingCallerWithReadReplicas.getRegionLocations(!reload,
        id, getConnection(), getTableName(), getRow());
  static RegionLocations getRegionLocations(boolean useCache, int replicaId,
                 ClusterConnection cConnection, TableName tableName, byte[] row)
      throws RetriesExhaustedException, DoNotRetryIOException, InterruptedIOException {
    RegionLocations rl;
    try {
      if (!useCache) {
        rl = cConnection.relocateRegion(tableName, row, replicaId);
      } else {
        rl = cConnection.locateRegion(tableName, row, useCache, true, replicaId);
      }
    }

cConnection.relocateRegion(tableName, row, replicaId); 看是不是有回到最开始的时候

而且根据结果获取ServerName()。然后将这个的变成目的地,
setStub(super.getConnection().getClient(dest));

到这里发现似乎一切又回到最开始。
有点抓狂,不过我还是发现了什么tableName 变成了TableName.META_TABLE_NAME,肯定变成前面table。所以一切都变了。还记得前面的分支吗。

if (tableName.equals(TableName.META_TABLE_NAME)) {
        return locateMeta(tableName, useCache, replicaId);
      } 

里面有个这样的
locations = this.registry.getMetaRegionLocation();
而这个registry 就是zookeeper的ZooKeeperRegistry

 static Registry getRegistry(final Connection connection)
  throws IOException {
    String registryClass = connection.getConfiguration().get(REGISTRY_IMPL_CONF_KEY,
      ZooKeeperRegistry.class.getName());
    Registry registry = null;
    try {
      registry = (Registry)Class.forName(registryClass).newInstance();
    } catch (Throwable t) {
      throw new IOException(t);
    }
    registry.init(connection);
    return registry;
  }

反正里面有个

   List<ServerName> servers = new MetaTableLocator().blockUntilAvailable(zkw, hci.rpcTimeout,
          hci.getConfiguration());

这样就重zk里拿到MATA的ServiceName。一切都通了。

总结
client 首先去zk 里拿到MATA的ServiceName 发送第一次callable.call(),然后 在MATA RS里拿到table的region的ServiceName.然后发送第二次callable。call.获得region信息。

这里只是分析了客户端代码,下一节分析RS端代码。

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值