客户端程序写数据通过HTable和Put进行操作,我们从客户端代码开始分析写数据的流程:
HTableInterface只是一个接口,所以最终调用的是它的子类HTable的put()方法。进入HTable.put():
从上面代码可以看出:你既可以一次put一行记录也可以一次put多行记录,两个方法内部都会调用doPut方法,最后再来根据autoFlush(默认为true),即自动提交,判断是否需要flushCommits刷写提交,在autoFlush为false的时候,如果当前容量超过了缓冲区大小(默认值为:2097152=2M),也会调用flushCommits方法。也就是说,在自动提交情况下,你可以手动控制通过一次put多条记录,然后将这些记录flush,以提高写操作吞吐量。
首先看下flushCommits()方法:
backgroundFlushCommits()方法,该方法会在后面讲到。
进入doPut()方法:
从上面的代码可以看出,backgroundFlushCommits()这个刷新操作可以是制定异步提交还是同步提交,从doPut方法中来看默认是以异步的方式进行,这里的ap是AsyncProcess类的一个实例,该类使用多线程的来实现异步的请求,也就是说,并非每一次put操作都是直接往HBase里面写数据的,而是等到缓存区域内的数据多到一定程度(默认设置是2M),再进行一次写操作。当然这次操作在Server端应当还是要排队执行的,具体执行机制这里不作展开。可以确定的是,HConnection在HTable的put操作中,只是起到一个定位RegionServer的作用,在定位到RegionServer之后,操作都是由cilent端通过rpc调用完成的。这个结论在插入/查询/删除中是一致的。
writeAsyncBuffer.add(put)就是向一个异步缓冲区添加该操作,然后当一定条件的时候进行flash,当发生flash操作的时候,才会真正的去执行该操作,这主要是提高系统的吞吐率,接下来我们去看看这个flush的操作内部。
看下waitUntilDone()方法:
进入waitForMaximumCurrentTasks()方法:
waitUntilDone()方法的操作流程,具体要等待到什么时候呢?等到tasksSent的值减去tasksDone的值等于0,tasksSent表示提交的任务数,tasksDone表示完成的任务数。
现在就可以重新总结一下backgroundFlushCommits()方法,在第965行,submit()方法传入的参数是true,表示需要等待rpc调用结束。第980行,如果有部分数据提交失败,同时没有设置清空失败的数据时,把数据重新添加到writeAsyncBuffer列表中。最后在finally块中,清空当前currentWriteBufferSize的大小,如果有数据没有提交成功,
重新把未提交的数据的大小计算起来添加到currentWriteBufferSize中。
lushCommits(),如果在doput的过程中,也就是调用htable.put(Put)的时候,如果缓存大小超过了客户端写缓存大小的限制,调用backgroundFlushCommits()方法方法是异步的;而在flushcommit方法中,backgroundFlushCommits()这个方法是同步的。
接下来就是重要的提交过程,submit()方法:
进入sendMultiAction()方法,看它是如何发送put请求的:
从上面的代码可以看出,每个任务都是通过HBase的RPC框架与服务器进行通信,并获取返回的结果。其中最重要的两个组件我用红色方框已经圈出,看下他俩的具体实现:
先构造一个MultiServerCallable,然后再通过rpcCallerFactory将其封装为RpcRetryingCaller做最后的call操作。
查看MultiServerCallable:
注释里就说的很明白了,client端通过MultiServerCallable.call()方法调用res的rpc的multi()方法,来实现put提交请求。可以想象,根据讲过的《Hadoop RPC机制-原理篇》,HRegionServer端必定也有一个multi()方法。
总结put操作:
(1)把put操作添加到writeAsyncBuffer队列里面,符合条件(自动flush或者超过了阀值writeBufferSize)就通过AsyncProcess异步批量提交。
(2)在提交之前,我们要根据每个rowkey找到它们归属的region server,这个定位的过程是通过HConnection的locateRegion方法获得的,然后再把这些rowkey按照HRegionLocation分组。在获得具体region位置的时候,会对最近使用的region server做缓存,如果缓存中保存了相应的region server信息,就直接使用这个region信息,连接这个region server,否则会对master进行一次rpc操作,获得region server信息,客户端的操作put、get、delete等操作每次都是封装在一个Action对象中进行提交操作的,都是一系列的的action一起提交,这就是MultiAction。
(3)通过多线程,一个HRegionLocation构造MultiServerCallable<Row>,然后通过rpcCallerFactory.<MultiResponse> newCaller()执行调用,忽略掉失败重新提交和错误处理,客户端的提交操作到此结束。
下篇文章将会介绍HRegionServer如何响应客户端发出的Put请求。