谈谈HTable的实现

server cache string byte table hbase

背景

HTable作为HBase的CRUD的客户端底层是怎么实现的,虽然HBase-The-Definitive-Guide 这本书的作者推荐在生产环境使用HTablePool
但了解HTable还是很有必要的

下面以一个简单的例子来说明
 

[java] view plain copy
  1. protected static String TEST_TABLE_NAME = "testtable";  
  2.  protected static String ROW1_STR = "row1";  
  3.  protected static String COLFAM1_STR = "colfam1";  
  4.  protected static String QUAL1_STR = "qual1";  
  5.  private final static byte[] ROW1 = Bytes.toBytes(ROW1_STR);  
  6.  private final static byte[] COLFAM1 = Bytes.toBytes(COLFAM1_STR);  
  7.  private final static byte[] QUAL1 = Bytes.toBytes(QUAL1_STR);  
  8.   
  9.  @Test  
  10.  public void testHTable() throws IOException {  
  11.   
  12.      Configuration conf = HBaseConfiguration.create();  
  13.      HTable table = new HTable(conf, TEST_TABLE_NAME);  
  14.   
  15.      Get get = new Get(ROW1);  
  16.      Result result = table.get(get);  
  17.      byte[] val = result.getValue(COLFAM1, QUAL1);  
  18.      assertThat(Bytes.toString(val), is("val1"));  
  19.  }  


代码非常简单,就是初始化HTable和调用HTable实例的get获取ROW1的值

在介绍下面的内容之前先简单的说下HBase server
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的变化
2)HTable的初始化step2
  • 从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
4)仔细看看HBaseClient.call
    • 初始化一个Call实例(封装了请求和响应)
    • 连接到Region server(见HBaseClient.getConnection)得到HBaseClient.Connection实例,此时会启动一个读线程(每个region一个),专门读取服务端的响应,也会将请求写入读线程的队列中
    • 发请求,见【HBaseClient.Connection.sendParam(call)】,如果是多个请求线程,由于此时共用一个连接,还存在同步问题,且网络写也是阻塞的,写完了还得等通知
    • 读线程会不断的读取响应,读取一个相应响应就将请求队列中对应的请求删除,并通知主线程即客户端线程,如果读线程空闲超时会自动回收
    • 客户端线程和读线程采用传统的wait + notify 通信,这也意味着客户端线程会阻塞

小结

  • 从HTable 的实现中看到了所谓的传说中的RPC到底是咋回事
    • 一个服务接口
    • 实现了服务接口的动态代理
    • 动态代理对应的invoke handle
    • 当然还有底层网络客户端
  • 另外也看到的HTable的io模型
    • 多个客户线程同步+阻塞写,并将写注册到读线程的队列中
    • 单线程绑定单连接(基本上是每个region对应一个),读取响应,删除队列中的请求标示,并通知客户线程
    • 并没有使用NIO 
    • 和传统的连接池以及时髦的使用select的事件驱动比起来以及是一个比较怪异的实现

你可能感兴趣的:(谈谈HTable的实现)