hbase put 流程分析client端

数据写入(Put)处理流程分析:

Put通过生成一个HTable实例,并调用其put方法时,的执行流程,此部分分析分为clientregionserver两个部分,

client:

Htable.put-->doPut,如果是put一个list时,会迭代调用doPut

privatevoiddoPut(Put put) throws InterruptedIOException,

RetriesExhaustedWithDetailsException {

检查上次提交是否出错,如果上次提交有错误,把这次的put添加到writeAsyncBuffer列表中。

并执行flush操作,等待flush完成操作

if (ap.hasError()){

writeAsyncBuffer.add(put);

backgroundFlushCommits(true);

}

put的内容进行检查:

1.检查put中是否指定cf,如果一个都没有,检查不合法

2.检查put中所有的kv中每一个kv的大小是否超过hbase.client.keyvalue.maxsize配置的值,默认-1表示不限制大小

validatePut(put);

把当前put的所有kv的大小,包含类定义大小加入到currentWriteBufferSize中,

此属性用来检查当前tablebuffer中的put大小是否超出了指定的buffersize

currentWriteBufferSize += put.heapSize();

把这次的put添加到writeAsyncBuffer列表中。

writeAsyncBuffer.add(put);

如果当前buffer中的put总大小超过了指定的table可存储的buffer大小时,进行flush,不等待flush完成操作。

flush过程中有可能writeAsyncBuffer的数据清空后submit出现错误,会把错误的put重新放入到此列表中。

while (currentWriteBufferSize > writeBufferSize) {

backgroundFlushCommits(false);

}

}

hbase.client.max.total.tasks,default=100

privatevoidbackgroundFlushCommits(booleansynchronous) throws

InterruptedIOException, RetriesExhaustedWithDetailsException {

 

try {

do {

writeAsyncBuffer列表中的数据通过RPC调用regionservermulti提交数据

提交过程中出现错误会throw InterruptedIOException

1.迭代并取出writeAsyncBuffer中的每一个put实例,与meta region所在的server创建rpc连接,

并从meta表中得到当前迭代的put对象row所在的HRegionLocation,

如果regionlocation获取失败,设置haserror=true,

在获取HRegionLocation时,先在cache中看是否能找到此regionLocation,

如果不能找到先得到meta regionlocation

生成meta rsrpc连接 ClientProtos.ClientService.BlockingInterface接口实现(HRegionServer)

通过client.getmeta region中得到当前putrow对应的region info,生成HRegionLocation,并添加到cache

2.通过hbase.client.max.perregion.tasks配置单个regionclient的并发数,默认为1

3.通过hbase.client.max.perserver.tasks配置单个regionserverclient的并发数,默认为2

4.通过hbase.client.max.total.tasks配置所有regionserver最大的并发连接任务个数,默认为100

检查任务数是否超过总可执行的任务数是通过tasksSent-tasksDone得到。

5.现在得到所有要put的数据对应的HRegionLocation列表,把每一个regionLocation对应要put的数据生成到

一个map<HRegionLocation,List<Row>>的集合列表中。

生成一个Action<Row>时,会相应的移出HTable.writeAsyncBuffer中对应的put实例。

在同一个regionserver中的所有region,它们的HRegionLocationequals都相同。

6.生成HConnectionManager.ServerErrorTracker实例,此实例

a.通过hbase.client.retries.number配置server的重试次数,默认为31

b.通过hbase.client.pause配置每次重试的间隔时间,默认100,每重试一次,超时时间会有相应延长.

7.针对每一个要提交的regionLocation(每一个region可能包含多个region),

a.tasksSent的值加一,表示总任务数加一,

b.regionLocation对应的regionservertaskCounterPerServer的值加一,表示此server的总任务加一

c.得到regionLocation中所有的region,把每一个region的任务数加一,taskCounterPerRegion值加一。

8.针对每一个regionLocation(每一个regionserver)创建一个rpc连接,并开始一个线程。

通过MultiServerCallable.call方法调用regionserver.multi方法添加数据。

9.每一个线程都提交rs后,等待rs的响应,如果提交失败,进行重试,直接timeout或重试次数到一定次数。

timeoutHConnectionManager.ServerErrorTracker实例生成时生成,具体请查看源代码。

每重试一次,timeout都会有相应的延长

10.根据每一个提交过去数据对应的region,把每一个regiontaskCounterPerRegion的值减一

taskCounterPerServer的值减一

tasksDone的值加一,表示完成一个任务,并把tasksDone进行notify.

Notify的目的是通知其它putsubmit任务结束等待,

submit方法最开始会检查是否等待,如果是tasksDonewait。具体请参见AsyncProcess.submit源代码。

 

ap.submit(writeAsyncBuffer, true);

} while (synchronous && !writeAsyncBuffer.isEmpty());

如果传入的参数是true,表示需要等待rpc调用结束,flushCommitsput中上一次提交error时此参数为true

等到tasksSent的值减去tasksDone的值等于0,tasksSent表示提交的任务数,tasksDone表示完成的任务数

if (synchronous) {

ap.waitUntilDone();

}

部分数据提交失败,也就是可能同时提交给两个regionserver,有一个成功,一个失败。

if (ap.hasError()) {

LOG.debug(tableName + ": One or more of the operations have failed -" +

" waiting for all operation in progress to finish (successfully or not)");

while (!writeAsyncBuffer.isEmpty()) {

ap.submit(writeAsyncBuffer, true);

}

等到tasksSent的值减去tasksDone的值等于0,tasksSent表示提交的任务数,tasksDone表示完成的任务数

ap.waitUntilDone();

如果有部分数据提交失败,同时没有设置清空失败的数据时,把数据重新添加到writeAsyncBuffer列表中

if (!clearBufferOnFail) {

// if clearBufferOnFailed is not set, we're supposed to keep the failed operation in the

// write buffer. This is a questionable feature kept here for backward compatibility

writeAsyncBuffer.addAll(ap.getFailedOperations());

}

RetriesExhaustedWithDetailsException e = ap.getErrors();

ap.clearErrors();

throwe;

}

} finally {

清空当前 currentWriteBufferSize的大小,如果有数据没有提交成功,

重新把未提交的数据的大小计算起来添加到 currentWriteBufferSize中。

 

currentWriteBufferSize = 0;

for (Rowmut : writeAsyncBuffer) {

if (mutinstanceofMutation) {

currentWriteBufferSize += ((Mutation) mut).heapSize();

}

}

}

}

你可能感兴趣的:(源代码,分布式,hbase)