談談HTable的實現

背景

HTable作爲HBase的CRUD的客戶端底層是怎麼實現的,雖然HBase-The-Definitive-Guide 這本書的作者推薦在生產環境使用HTablePool
但瞭解HTable還是很有必要的

下面以一個簡單的例子來說明
 

  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的事件驅動比起來以及是一個比較怪異的實現
發佈了102 篇原創文章 · 獲贊 10 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章