1.原由(Why HBase Coprocessor)java
HBase做爲列族數據庫最常常被人詬病的特性包括:沒法輕易創建「二級索引」,難以執行求和、計數、排序等操做。好比,在舊版本的(<0.92)Hbase中,統計數據表的總行數,須要使用Counter方法,執行一次MapReduce Job才能獲得。雖然HBase在數據存儲層中集成了MapReduce,可以有效用於數據表的分佈式計算。然而在不少狀況下,作一些簡單的相加或者聚合計算的時候,若是直接將計算過程放置在server端,可以減小通信開銷,從而得到很好的性能提高。因而,HBase在0.92以後引入了協處理器(coprocessors),實現一些激動人心的新特性:可以輕易創建二次索引、複雜過濾器(謂詞下推)以及訪問控制等。sql
2.靈感來源( Source of Inspration)數據庫
HBase協處理器的靈感來自於Jeff Dean 09年的演講( P66-67)。它根據該演講實現了相似於bigtable的協處理器,包括如下特性:apache
每一個表服務器的任意子表均可以運行代碼 編程
客戶端的高層調用接口(客戶端可以直接訪問數據表的行地址,多行讀寫會自動分片成多個並行的RPC調用) 服務器
提供一個很是靈活的、可用於創建分佈式服務的數據模型 架構
可以自動化擴展、負載均衡、應用請求路由 併發
HBase的協處理器靈感來自bigtable,可是實現細節不盡相同。HBase創建了一個框架,它爲用戶提供類庫和運行時環境,使得他們的代碼可以在HBase region server和master上處理。負載均衡
3.細節剖析(Implementation)框架
協處理器分兩種類型,系統協處理器能夠全局導入region server上的全部數據表,表協處理器便是用戶能夠指定一張表使用協處理器。協處理器框架爲了更好支持其行爲的靈活性,提供了兩個不一樣方面的插件。一個是觀察者(observer),相似於關係數據庫的觸發器。另外一個是終端(endpoint),動態的終端有點像存儲過程。3.1觀察者(Observer)
觀察者的設計意圖是容許用戶經過插入代碼來重載協處理器框架的upcall方法,而具體的事件觸發的callback方法由HBase的核心代碼來執行。協處理器框架處理全部的callback調用細節,協處理器自身只須要插入添加或者改變的功能。
以HBase0.92版本爲例,它提供了三種觀察者接口:
RegionObserver:提供客戶端的數據操縱事件鉤子:Get、Put、Delete、Scan等。
WALObserver:提供WAL相關操做鉤子。
MasterObserver:提供DDL-類型的操做鉤子。如建立、刪除、修改數據表等。
這些接口能夠同時使用在同一個地方,按照不一樣優先級順序執行.用戶能夠任意基於協處理器實現複雜的HBase功能層。HBase有不少種事件能夠觸發觀察者方法,這些事件與方法從HBase0.92版本起,都會集成在HBase API中。不過這些API可能會因爲各類緣由有所改動,不一樣版本的接口改動比較大,具體參考Java Doc。
RegionObserver工做原理,如圖1所示。更多關於Observer細節請參見HBaseBook的第 9.6.3章節。
3.2終端(Endpoint)
終端是動態RPC插件的接口,它的實現代碼被安裝在服務器端,從而可以經過HBase RPC喚醒。客戶端類庫提供了很是方便的方法來調用這些動態接口,它們能夠在任意時候調用一個終端,它們的實現代碼會被目標region遠程執行,結果會返回到終端。用戶能夠結合使用這些強大的插件接口,爲HBase添加全新的特性。終端的使用,以下面流程所示:
單個region:HTableInterface.coprocessorProxy(Class protocol, byte[] row) 。 rigons區域:HTableInterface.coprocessorExec(Class protocol, byte[] startKey, byte[] endKey, Batch.Call callable)
總體的終端調用過程範例,所示:
4.編程實踐(Code Example)
在該實例中,咱們經過計算HBase表中行數的一個實例,來真實感覺協處理器 的方便和強大。在舊版的HBase咱們須要編寫MapReduce代碼來彙總數據表中的行數,在0.92以上的版本HBase中,只須要編寫客戶端的代碼便可實現,很是適合用在WebService的封裝上。
4.1啓用協處理器 Aggregation(Enable Coprocessor Aggregation)
咱們有兩個方法:
1.啓動全局aggregation,能過操縱全部的表上的數據。經過修改hbase-site.xml這個文件來實現,只須要添加以下代碼:
<property> <name>hbase.coprocessor.user.region.classes</name> <value>org.apache.hadoop.hbase.coprocessor.AggregateImplementation</value> </property>
2.啓用表aggregation,只對特定的表生效。經過HBase Shell 來實現。
disable 'xyz' alter 'xyz','coprocessor'=>'|org.apache.hadoop.hbase.coprocessor.example.RowCountEndpoint||' enable 'xyz'
package hbase.learn.com; import java.util.Map; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.HTable; public class RowCount { public static void main(String[] args) { String tableName="xyz"; // Get instance of Configuration Configuration configuration = HBaseConfiguration.create(); // Get table instance HTable table = new HTable(configuration, tableName); final ExampleProtos.CountRequest request = ExampleProtos.CountRequest.getDefaultInstance(); Map<byte[],Long> results = table.coprocessorService(ExampleProtos.RowCountService.class, null, null, new Batch.Call<ExampleProtos.RowCountService,Long>() { public Long call(ExampleProtos.RowCountService counter) throws IOException { ServerRpcController controller = new ServerRpcController(); BlockingRpcCallback<ExampleProtos.CountResponse> rpcCallback = new BlockingRpcCallback<ExampleProtos.CountResponse>(); counter.getRowCount(controller, request, rpcCallback); ExampleProtos.CountResponse response = rpcCallback.get(); if (controller.failedOnException()) { throw controller.getFailedOn(); } return (response != null && response.hasCount()) ? response.getCount() : 0; } }); long sum=0; int count=0; for(Long l:results.values()){ sum+=l; count++; } System.out.println("row count = "+sum); System.out.println("region count = "+count); } }
歡迎工做一到五年的Java工程師朋友們加入Java架構開發: 855835163
羣內提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!