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;