HBase源码分析之Client端操作处理

本文深入分析HBase客户端连接建立过程,从Connection接口和HTable的实现入手,探讨Get、Delete、Put及Scan等操作的执行流程。讲解了包括RegionServerCallable在内的重要组件,以及批量操作、异步写入、并发控制和性能优化策略。
摘要由CSDN通过智能技术生成
hbase version 1.1.5

使用HBase的服务要先建立与HBase服务器的连接,这里我们从hbase client的建立连接开始HBase源码分析的旅程。
先看一段hbase连接的代码

Connection connection = ConnectionFactory.createConnection(config);
Table table = connection.getTable(TableName.valueOf("table1"));
try {
    // Use the table as needed, for a single operation and a single thread
} finally {
    table.close();
    connection.close();
}

Connection接口

Connection接口有定义以下常用方法

Table getTable(TableName tableName)  // 返回一个Table的实现(HTable),用于访问hbase的表
public RegionLocator getRegionLocator(TableName tableName)  //返回表的region信息
Admin getAdmin() //返回Admin的实现来管理HBase集群

这里我们先分析Table接口以及其实现HTable,先看看Table接口定义的方法,hbase table的所有的操作都定义在这了。

boolean exists(Get get) 
Result get(Get get)
Result[] get(List<Get> gets) 
ResultScanner getScanner(Scan scan) 
void put(Put put) 
void put(List<Put> puts) 
void delete(Delete delete)
void mutateRow(final RowMutations rm)

HTable的实现

Get

接下来我们来看HTable的实现,首先我们从Get方法开始
先看最简单的单个Get请求

// HTable.java
 private Result get(Get get, final boolean checkExistenceOnly) throws IOException {
    // if we are changing settings to the get, clone it.
   ...

    if (get.getConsistency() == Consistency.STRONG) {
      // Good old call.
      final Get getReq = get;
      RegionServerCallable<Result> callable = new RegionServerCallable<Result>(this.connection,
          getName(), get.getRow()) {
        @Override
        public Result call(int callTimeout) throws IOException {
          ClientProtos.GetRequest request =
            RequestConverter.buildGetRequest(getLocation().getRegionInfo().getRegionName(), getReq);
          PayloadCarryingRpcController controller = rpcControllerFactory.newController();
          controller.setPriority(tableName);
          controller.setCallTimeout(callTimeout);
          try {
            ClientProtos.GetResponse response = getStub().get(controller, request);
            if (response == null) return null;
            return ProtobufUtil.toResult(response.getResult());
          } catch (ServiceException se) {
            throw ProtobufUtil.getRemoteException(se);
          }
        }
      };
      return rpcCallerFactory.<Result>newCaller(rpcTimeout).callWithRetries(callable,
          this.operationTimeout);
    }

    // Call that takes into account the replica
    RpcRetryingCallerWithReadReplicas callable = new RpcRetryingCallerWithReadReplicas(
      rpcControllerFactory, tableName, this.connection, get, pool,
      connConfiguration.getRetriesNumber(),
      operationTimeout,
      connConfiguration.getPrimaryCallTimeoutMicroSecond());
    return callable.call();
  }

通过代码可以看出get请求直接构造了一个RegionServerCallable对象,通过RegionServerCallable的文档说该类是实现一个到RegionServer的调用,那怎么确定是哪个RegionServer和怎么和RegionServer建立连接的呢?我们来看看RegionServerCallable的prepare方法

// RegionServerCallable.java
 /**
   * Prepare for connection to the server hosting region with row from tablename.  Does lookup
   * to find region location and hosting server.
   * @param reload Set this to true if connection should re-find the region
   * @throws IOException e
   */
  @Override
  public void prepare(final boolean reload) throws IOException {
    try (RegionLocator regionLocator = connection.getRegionLocator(tableName)) {
      this.location = regionLocator.getRegionLocation(row, reload);
    }
    if (this.location == null) {
      throw new IOException("Failed to find location, tableName=" + tableName +
        ", row=" + Bytes.toString(row) + ", reload=" + reload);
    }
    setStub(getConnection().getClient(this.location.getServerName()));
  }

通过这里我们可以看出通过regionLocator.getRegionLocation 获取row所属region的regionserver的地址信息。再通过调用HConnetion.getClient 建立和对应regionserver的连接,返回一个regionserver的ClientProtocol proxy 。这样我们就可以封装一个请求和regionserver通信了。调用regionserver rpcserver的get方法。

// HRegionLocation.java
public class HRegionLocation implements Comparable<HRegionLocation> {
   
  private final HRegionInfo regionInfo;
  private final ServerName serverName;
  private final long seqNum;

这里单个的Get方法的客户端流程就走完了。下面我们来看看传入一个List的Get方法

// HTable.java
public Result[] get(List<Get> gets) throws IOException {
    if (gets.size() == 1) {
      return new Result[]{get(gets.get(0))};
    }
    try {
      Object [] r1 = batch((List)gets);

      // translate.
      Result [] results = new Result[r1.length];
      ...
    }
  }

实际上这里就是调用了batch方法,

// HTable.java
protected AsyncProcess multiAp;

public void batch(final List<? extends Row> actions, final Object[] results)
      throws InterruptedException, IOException {
    AsyncRequestFuture ars = multiAp.submitAll(pool, tableName, actions, null, results);
    ars.waitUntilDone();
    if (ars.hasError()) {
      throw ars.getErrors();
    }
  }

这里通过AsyncProcess.submitAll方法来提交操作请求

// AsyncProcess.java
public <CResult> AsyncRequestFuture submitAll(ExecutorService pool, TableName tableName,
      List<? extends Row> rows, Batch.Callback<CResult> callback, Object[] results) {
    List<Action<Row>> actions = new ArrayList<Action<Row>>(rows.size());

    // The position will be used by the processBatch to match the object array returned.
    int posInList = -1;
    NonceGenerator ng = this.connection.getNonceGenerator();
    for (Row r : rows) {
      posInList++;
      if (r instanceof Put) {
        Put put = (Put) r;
        
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值