1、java使用HTable创建客服端
最开始的想法是做一个HTable池,对同一张表创建多个HTable:
config = new Configuration();
config.set("hbase.zookeeper.quorum", "127.0.0.1"));
config.set("hbase.zookeeper.property.clientPort", "2181"));
HTable table1 = new HTable(conf, "myTable");
HTable table2 = new HTable(conf, "myTable");
似乎是没有问题,我做测试的时候也是符合预想的。但是最后部署在服务器,在并发访问的情况下,服务器出现cpu被占用完的情况(gc任务占用)。
(1)、第一次定位问题
经过不断的调试最后定位在HTable池的问题,测试发现只有在对同一张表创建唯一HTable时才不会出现问题。在(http://blog.csdn.net/u010967382/article/details/38046821)中得到config的属性相同则CONNECTION_INSTANCES.get返回同一个connection实例,个人认为与connection唯一有关。如果使用创建多个config也是线程不安全的,具体还不知道,只有求教各位大神!!!
(2)、第二次定位问题
经过严格的测试发现依然没有解决问题。看到博客(http://blog.csdn.net/pwlazy/article/details/7417135)如下:
HBase server 基本上由zookeeper和HRegion server组成
前者主要是用于监控和管理HRegion server,后者才是提供数据服务的
HRegion server又分为master+server,master用于维护meta信息,
master和server的确定使用了leader follow方式,任何HRegion server都有可能成为Master
HTable模型
- HTable: 客户端API
- HConnection: HTable对zookeeper的网络连接
- ServerCallable:抽象一次和Region Server的交互,因此它需要有HConnection和HRegionLocation,同时需要有一个访问Region Server的接口
- HRegionInterface HRegionLocation: Region Server的地址
- HRegionInfo: Region Server的信息
- HRegionInterface:访问Region Server的接口的动态代理
- WritableRpcEngine$Invoker:动态代理的相关handle
- HBaseClient:Region Server的客户端
- 其他的就不解释了
HTable runtime
1)HTable的初始化step1
- 初始化对zookeeper的连接
- 会调用zookeeper的api,启动两个线程,一个是sendthread,一个是eventhtread,
- sendthread专门维护对zookeeper的连接,并且会监听zookeeper 的事件,比如节点变化的通知,一旦接到通知会将通知转发给eventhtread
- eventhtread处理zookeeper的变化
- 从zookeeper获取到root HRegionLocation[region=-ROOT-,,0.70236052, hostname=localhost, port=33032] 并cache
- 从root HRegion中获取到meta HRegionLocation[region=.META.,,1.1028785192, hostname=localhost, port=33032] 并cache
- 从meta HRegion 取更多hregion信息缓存到本地(见HConnectionManager$HConnectionImplementation.prefetchRegionCache)
- 从meta HRegion中获取表所在的HRegionLocation[region=testtable,,1332256150817.5c8417fb964377174f649af04a70ad5e., hostname=localhost, port=33032]并cache
3) HTable.get到底干了什么
- 从cache中获取表对应的HRegionLocation
- 从cache中获取HRegionLocation对应的Region server【一个实现了HRegionInterface的动态代理,使用了WritableRpcEngine$Invoker作为InvocationHandler】
- 调用动态代理的get,解下来就会调用WritableRpcEngine$Invoker的invoke
- 调用HBaseClient.call
- 初始化一个Call实例(封装了请求和响应)
- 连接到Region server(见HBaseClient.getConnection)得到HBaseClient.Connection实例,此时会启动一个读线程(每个region一个),专门读取服务端的响应,也会将请求写入读线程的队列中
- 发请求,见【HBaseClient.Connection.sendParam(call)】,如果是多个请求线程,由于此时共用一个连接,还存在同步问题,且网络写也是阻塞的,写完了还得等通知
- 读线程会不断的读取响应,读取一个相应响应就将请求队列中对应的请求删除,并通知主线程即客户端线程,如果读线程空闲超时会自动回收
- 客户端线程和读线程采用传统的wait + notify 通信,这也意味着客户端线程会阻塞