HBase2.0.1源码解析——根据RowKey定位Region的过程

  |   0 评论   |   2,575 浏览

在org.apache.hadoop.hbase.client.ConnectionImplementation中有多个*locateRegion*方法,用来查找Region所在的RegionLocation,区别在于条件的不同:

  • 是否包含RowKey

  • 是否使用缓存

  • 是否重试

  • 是否包含replicaId

根据表名确定定位的方式

org.apache.hadoop.hbase.client.ConnectionImplementation#locateRegion

查找指定tableName和row所在的Region的位置,useCache默认true,retry默认true,replicaId默认为0

如果表名是hbase:meta,就查找zookeeper,否则就扫描meta表来定位RegionLocation

image.png

定位Meta表

总体流程

org.apache.hadoop.hbase.client.ConnectionImplementation#locateMeta

  1. 对缓存进行double-check,确保缓存可以命中时就不请求Zookeeper

  2. 如果两次缓存都没有命中,则请求Zookeeper来获取RegionLocation

image.png

查找缓存

metaCache使用一个CopyOnWriteArrayMap<TableName, ConcurrentNavigableMap<byte[], RegionLocations>>来存储RegionLocations信息

org.apache.hadoop.hbase.client.MetaCache#getCachedLocation

利用java.util.NavigableMap#floorEntry方法查找小于等于给定键的最大键所对应的元素

image.png

查找Zookeeper

org.apache.hadoop.hbase.client.ZKAsyncRegistry#getMetaRegionLocation

由于meta的Region可能有多个副本,因此遍历/hbase/meta-region-server-i,返回所有副本对应的ServerName

@Override
public CompletableFuture<RegionLocations> getMetaRegionLocation() {
CompletableFuture<RegionLocations> future = new CompletableFuture<>();
HRegionLocation[] locs = new HRegionLocation[znodePaths.metaReplicaZNodes.size()];
MutableInt remaining = new MutableInt(locs.length);
// 遍历(i, /hbase/meta-region-server-i)
znodePaths.metaReplicaZNodes.forEach((replicaId, path) -> {
  if (replicaId == DEFAULT_REPLICA_ID) {  //主副本
getAndConvert(path, ZKAsyncRegistry::getMetaProto).whenComplete((proto, error) -> {
  if (error != null) {
future.completeExceptionally(error);
return;
  }
  if (proto == null) {
future.completeExceptionally(new IOException("Meta znode is null"));
return;
  }
  Pair<RegionState.State, ServerName> stateAndServerName = getStateAndServerName(proto);
  if (stateAndServerName.getFirst() != RegionState.State.OPEN) {
future.completeExceptionally(
  new IOException("Meta region is in state " + stateAndServerName.getFirst()));
return;
  }
  locs[DEFAULT_REPLICA_ID] =
  new HRegionLocation(getRegionInfoForDefaultReplica(FIRST_META_REGIONINFO),
  stateAndServerName.getSecond());
  tryComplete(remaining, locs, future);
});
  } else {
getAndConvert(path, ZKAsyncRegistry::getMetaProto).whenComplete((proto, error) -> {
  if (future.isDone()) {
return;
  }
  if (error != null) {
LOG.warn("Failed to fetch " + path, error);
locs[replicaId] = null;
  } else if (proto == null) {
LOG.warn("Meta znode for replica " + replicaId + " is null");
locs[replicaId] = null;
  } else {
Pair<RegionState.State, ServerName> stateAndServerName = getStateAndServerName(proto);
if (stateAndServerName.getFirst() != RegionState.State.OPEN) {
  LOG.warn("Meta region for replica " + replicaId + " is in state " +
  stateAndServerName.getFirst());
  locs[replicaId] = null;
} else {
  locs[replicaId] =
  new HRegionLocation(getRegionInfoForReplica(FIRST_META_REGIONINFO, replicaId),
  stateAndServerName.getSecond());
}
  }
  tryComplete(remaining, locs, future);
});
  }
});
return future;
}

在hbase:meta表中定位HRegionLocation信息

总体流程

org.apache.hadoop.hbase.client.ConnectionImplementation#locateRegionInMeta

方法比较长,分段来看

首先查看缓存,来确定我们是否已经拥有该region

image.png

构造对meta表的Scan对象

这个地方使用了反向扫描及服务端的Pread读

image.png

带重试的扫描meta的Region

最大重试次数,由参数hbase.client.retries.number指定,默认为15次

image.png

构造ReversedClientScanner,进行扫描

遍历Scan的结果,把结果转换为我们需要的HRegionLocation,然后放入缓存中

ReversedClientScanner rcs =
  new ReversedClientScanner(conf, s, TableName.META_TABLE_NAME, this, rpcCallerFactory,
  rpcControllerFactory, getMetaLookupPool(),
  metaReplicaCallTimeoutScanInMicroSecond)) {
boolean tableNotFound = true;
for (;;) {
  Result regionInfoRow = rcs.next();
  if (regionInfoRow == null) {
if (tableNotFound) {
  throw new TableNotFoundException(tableName);
} else {
  throw new IOException(
"Unable to find region for " + Bytes.toStringBinary(row) + " in " + tableName);
}
  }
  tableNotFound = false;
  // 把结果转换为我们需要的HRegionLocation
  RegionLocations locations = MetaTableAccessor.getRegionLocations(regionInfoRow);
  if (locations == null || locations.getRegionLocation(replicaId) == null) {
throw new IOException("RegionInfo null in " + tableName + ", row=" + regionInfoRow);
  }
  RegionInfo regionInfo = locations.getRegionLocation(replicaId).getRegion();
  if (regionInfo == null) {
throw new IOException("RegionInfo null or empty in " + TableName.META_TABLE_NAME +
  ", row=" + regionInfoRow);
  }
  // 即使children在线,我们也可能找到分离的父region,所以在这里我们需要跳过这个region并转到下一个region。
  if (regionInfo.isSplitParent()) {
continue;
  }
  if (regionInfo.isOffline()) {
throw new RegionOfflineException("Region offline; disable table call? " +
regionInfo.getRegionNameAsString());
  }
  // 分裂的孩子可能还没有在线,我们在上述情况下跳过了父region,所以我们可能已经到达了一个不包含我们的region。
  if (!regionInfo.containsRow(row)) {
throw new IOException(
  "Unable to find region for " + Bytes.toStringBinary(row) + " in " + tableName);
  }
  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));
  }
  if (isDeadServer(serverName)) {
throw new RegionServerStoppedException(
  "hbase:meta says the region " + regionInfo.getRegionNameAsString() +
" is managed by the server " + serverName + ", but it is dead.");
  }
  // 将新发现的HRegionLocation放入缓存中。
  cacheLocation(tableName, locations);
  return locations;
}
}


读后有收获可以支付宝请作者喝咖啡