深入理解HBase Indexer

1. 簡介

Hbase Indexer全名爲Lily hbase Indexer,是NGDATA公司爲了將lily子系統裏面相關HBase數據存儲到Solr而開發的一個軟件。NGDATA公司將源代碼開源並託管在Github上,通過以下Github地址訪問Hbase-Indexer項目主頁及代碼: 
https://github.com/NGDATA/hbase-indexer

2. 原理

HBase Indexer的主要作用是將HBase表裏面的某些列(或者所有列)數據近乎實時地索引到SolrCloud裏面。爲什麼說近乎實時?因爲HBase Indexer是依賴於HBase的replication功能來實現將數據索引到Solr裏面的。HBase Indexer通過監聽HBase WAL日誌(WAL日誌裏面記錄了所有對數據的增刪改操作),將增刪改操作事件轉換成對應的Solr增刪改操作。由於這個過程是異步進行的,並且重放HBase WAL日誌本身就存在一定的延遲,所以才說HBase Indexer是近乎實時地將數據索引到Solr裏面。(我們可以類比MySQL master slave架構,slave與master之間也會存在一定的延遲。)

HBase Indexer集羣可以看成是HBase高可用方案裏面的Slave Cluster。下圖展示了HBase Indexer在HBase集羣和Solr集羣中的地位:

hbase-indexer-structure

HBase Indexer的地位類似於HBase高可用方案裏面的Slave集羣,事實上HBase Indexer上每一個indexer(底層核心是SepConsumer類)都是一個僞裝的、弱化了的RegionServer,負責接收SepEvent,並處理自己感興趣的那部分數據,然後將數據存儲到Solr集羣上。

爲什麼選擇Replication而不選擇Coprocessor來實現HBase Indexer?

1)HBase Replication的處理是由RegionServer開啓獨立的線程去處理的,處理方式是並行且異步的,依靠這種機制來實現HBase Indexer並不會給HBase帶來入侵式的代碼,而且不會影響寫入性能。而通過Coprocessor來實現的話會給RegionServer帶來入侵式代碼,假如我們實現的邏輯出現問題,很可能會影響到HBase集羣的性能(Coprocessor的實現至少會影響HBase集羣的寫性能),嚴重的情況還會導致整個HBase集羣掛掉。

2)雖然選擇Replication機制只能實現近實時的索引同步,但是這種實現方式具備很高的靈活性和可擴展性,最重要的是它對HBase集羣的使用是幾乎沒有侵佔性的,不會影響HBase集羣的寫性能(當然開啓Replication會對HBase集羣性能造成一定影響)

3. HBase Indexer核心 ——SEP

3.1 SEP介紹

SEP,即Side-Effect Processor,是實現HBase Indexer功能的核心子模塊,項目源碼在hbase-sep模塊裏面。SEP的主要功能是:監聽HBase集羣上發生的一切數據變化,然後對這些變化做出處理。這裏說的數據變化是記錄在WAL日誌裏面的,所以SEP要做的就是監聽WAL日誌變化,並對日誌變化做出響應:將這些變化封裝成一個SepEvent集合,交給EventListener去處理。而HBase-Indexer所做的就是實現EventListener接口(實現類爲IndexingEventListener)裏面的processEvents方法,在這個方法裏面處理數據並將數據索引到solr裏面。

cloudera-hbase-indexer-structure

注意,SEP並不關心WAL日誌是怎麼被讀取的,因爲這部分處理是由HBase Replication System提供的。SEP就相當於設計模式裏面的監聽者,而HBase Replication System則負責將事件通知給對應的監聽者SEP。

如果我們將HBase-Indexer部署到多臺機器上面,那麼當需要進行replication的表數據變動的時候,HBase Replication System每次都會隨機選擇一臺HBase Indexer作爲接收方來發送通知(SepEvent),前提是在hbase-site.xml(HBase集羣配置)裏面配置了replication.source.ratio = 1。HBase Replication確保重放WAL日誌是順序的,以此來保證存儲到Solr集羣的數據跟HBase最新的數據是一致的。但是,對於SEP,其內部會採用多線程來處理(消費)這些WAL日誌,以提高效率。(我們可以把這種模式想象成生產者消費者模式,其中SEP對應於消費者,而消費者爲了提高處理效率採用了多線程方式來處理事件)

3.2 SepConsumer

實現SEP功能的主類是SepConsumer。SepConsumer負責與HBase集羣上的RegionServer通訊並接收WAL日誌事件,使用內部線程池對日誌事件進行處理,真正的處理需要由用戶自定義。用戶要實現WAL日誌事件的處理只需要實現EventListener接口,在processEvents方法裏面進行處理。HBase-Indexer所做的就是實現EventListener接口(實現類爲IndexingEventListener)裏面的processEvents方法,在這個方法裏面處理數據並將數據索引到solr裏面。

3.2.1 SepConsumer線程模型

sepconsumer-thread-model

一個SepConsumer主要包括與HBase集羣通訊的RpcServer和處理SepEvents的ThreadPoolExecutors,其中RpcServer會消耗大概20個線程左右,而處理SepEvents的線程池大小是可控制的,由hbaseindexer.indexer.threads參數決定,默認爲10個,這裏的10個是指10個線程池,但是每個線程池只使用1個工作線程,並且工作隊列使用ArrayBlockingQueue,容量爲100,RejectedPolicy選擇WaitPolicy。因此,當線程池裏面的工作隊列滿了,後面的作業將繼續等待。總的來說,一個SepConsumer將使用將近30個線程,可以看出SepConsumer對於線程資源的要求是挺高的。

3.2.2 EventListener

SepConsumer將SepEvent委託給EventListener的實現類進行處理,EventListener接口僅僅定義了一個processEvents方法。而Hbase Indexer正是通過實現EventListener接口(實現類爲IndexingEventListener)的processEvents方法來處理數據映射並同步到Solr。

4. 主要流程分析

4.1 HBase Indexer入口分析

HBase Indexer的入口在hbase-indexer-server模塊的Main類裏面,入口實現的動作有:

1) 使用後臺線程方式啓動hbase-indexer 
2) 創建配置信息,主要包括hbase以及hbase-indexer的配置信息 
3) 如果配置了Ganglia進行性能監控,那麼啓動該監控 
4) 處理IndexerMaster的選舉和啓動,接下來由IndexerSupervisor啓動所有註冊了的Indexer,然後保存所有成功啓動的Indexer的註冊信息,最後使用Jetty啓動http服務(主要是爲了將indexer註冊信息發佈成REST服務)。

注意,所有成功啓動的Indexer將僞裝成一臺Slave RegionServer,去監聽HBase RegionServer集羣所產生的數據變化(WAL日誌)。

4.2 IndexerMaster啓動流程

一個HBase Indexer集羣只能有一臺HBase Indexer競選爲master(IndexerMaster)。IndexerMaster的主要功能有:管理並維護集羣上indexer的增加、更新、刪除,協調MapReduce作業的運行。(主要是通過MapReduce來進行HBase數據的全量索引)

IndexerMaster的啓動流程主要就是競選master的過程,競選成功的那臺HBase Indexer將啓動EventWork線程負責監聽集羣上indexer事件,包括增加、更新、刪除以及MapReduce作業的提交。而競選不到的master就不負責這些事情,但是會等待下一次競選的到來。(防止已有的master掛掉,再次參與選舉)

4.3 增刪改Indexer處理流程

前面提到增、刪、改Indexer最終是由競選爲master的IndexerMaster負責處理的,具體邏輯在內部類EventWorker的實現裏面。IndexerMaster上的EventWorker會使用一個阻塞隊列接收IndexerModelEvent(代表Indexer增刪改事件),根據這些事件的類型(INDEXER_ADDED INDEXER_UPDATED INDEXER_DELETED)作出相應處理。

那麼IndexerModelEvent是怎麼來的呢?實際上,我們發出的增刪改Indexer操作首先會在zookeeper上創建相應的路徑和節點。而IndexModelImpl的IndexModelChangeWatcher會監聽這些節點的變化,從而喚醒IndexerCacheRefresher線程觸發刷新事件,IndexerCacheRefresher會把節點變化封裝成IndexerModelEvent並交給IndexerModelListener進行處理。而IndexerModelListener最終將事件委託給IndexerMaster的EventWorker去處理。

處理流程如下圖所示:

flow_of_indexer_process

4.4 WAL事件處理流程

前面提到,Indexer實際上是一個僞裝的Slave RegionServer,負責監聽來自HBase RegionServer產生的數據變化。Indexer的底層核心實現是SepConsumer類,HBase RegionServer對於產生的數據變化,會隨機選擇一臺HBase Indexer進行WAL日誌的發送。那麼將會通知這臺HBase Indexer上的所有Indexer,最終調用SepConsumer類的replicateLogEntries方法。(注意高版本的HBase這個方法名有變化)因此WAL事件處理就集中在SepConsumer類的replicateLogEntries方法裏面。這個方法完成的動作主要是:將接收到的WAL日誌封裝成SepEvent,傳遞給SepEventExecutor進行處理,當累計到一定的數據量,進行一次batch操作。(SepEventExecutor.scheduleSepEvent方法的調用)最終對SepEvent事件的處理將委託給EventListener.processEvents方法進行處理。

注意,EventListener是構造SepConsumer必須傳遞的對象,HBase Indexer也正是實現了EventListener接口來自定義對SepEvent的處理:將數據經過映射處理後索引到Solr裏面。實現了EventListener的類是IndexingEventListener。

處理流程如下圖所示:

flow_of_wal_process

5.HBase Indexer評估

經過對HBase Indexer重要模塊源碼的初步瞭解以及實際的案例測試,可以發現HBase Indexer的使用代價比較高,主要體現在:

1)HBase RegionServer選擇一個HBase Indexer節點進行WAL日誌重放的時候,該HBase Indexer上所有的Indexer(SepConsumer)都會同時監聽到這些日誌,然後再從這些日誌過濾出符合自己所要索引的那部分數據進行處理。所以,如果我們創建的indexer非常多的話,很多情況下大部分indexer都沒有捕獲到自己感興趣的日誌,那麼會產生比較大的性能損耗。

2)每一個Indexer(SepConsumer)將會佔用至少30個線程,包括與HBase RegionServer通訊的RPC線程,Indexer處理日誌的線程等。因此如果我們創建的indexer比較多並且很多indexer同時在線的情況下,對資源的佔用可想而知是多麼大。其中最主要的資源是CPU和內存。

3)我們在測試案例中發現如果有大數據量的數據導入HBase或者大量的數據從HBase中被刪除,那麼會引起HBase Indexer集羣一直處於繁忙狀態,那麼這個時候Solr的數據同步可能存在較大的延遲。綜合1)和2)我們不難發現這一點。

綜合以上情況考慮,在設備允許的情況下,HBase Indexer應該部署在單獨的集羣上面,與HBase、Solr等集羣隔離開,避免資源競爭明顯的現象。

6. 參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章