近期這段時間在設計和實現日誌系統。在整個日誌系統系統中Zookeeper的做用很是重要——它用於協調各個分佈式組件並提供必要的配置信息和元數據。這篇文章主要分享一下Zookeeper的使用場景。css
這裏主要涉及到Zookeeper在日誌系統中的使用,但事實上它在咱們的消息總線和搜索模塊中也相同很是重要。node
日誌的類型和日誌的字段這裏咱們統稱爲日誌的元數據。咱們構建日誌系統的目的終於主要是爲了:日誌搜索,日誌分析。這兩大塊咱們很是大程度上依賴於——ElasticSearch(關於什麼是ElasticSearch,這裏就很少作介紹了)。mysql
日誌的字段定義,在ElasticSearch中是一個索引中某個mapping的Schema。ElasticSearch是一個號稱Schema Free
的分佈式全文檢索系統。這裏需要正確地理解Schema Free
,它並不是不需要Schema。也不是強制要求你必須有明白的Schema,有沒有都可以作全文檢索。但是像聚合、分析等很是多高級功能都創建在明白的Schema的基礎上。所以。從分析統計的角度看,咱們對日誌進行明白的字段定義是很是有必要的。nginx
日誌元數據是日誌系統的基礎信息。咱們在web管控臺管理它並同步至Zookeeper供其它模塊使用。比方搜索模塊。因爲上文提到日誌的類型、字段事實上跟ElasticSearch的Mapping Type
是對等的映射關係。因此搜索模塊會重度依賴日誌元數據。git
另外。爲了保證新的日誌元數據(通常是一個新的日誌類型被建立)儘快同步至ElasticSearch。咱們利用了Zookeeper的事件Push機制(或者叫Pub/Sub機制)來實時獲知日誌元數據的變化。一旦有新的日誌元數據產生。咱們的搜索模塊會立刻獲得事件通知,它會獲取最新的日誌元數據。而後爲其在ElasticSearch的indices中新建一個mapping。爲這樣的日誌類型的日誌存入ElasticSearch作好準備。github
這樣的方式帶來了哪些優勢。眼下來看至少有三點:web
從以前的文章中,你應該可以找到日誌採集器的選型,出於多種緣由(跟消息總線的集成、可定製性、支持從zookeeper獲取配置等),咱們選擇了Flume-ng。它在最新的穩定版(1.6.0)提供了從zookeeper獲取採集配置的功能。sql
因而,日誌採集花費在運維上的成本就大大減小了。因爲有了配置以後,運維人員就不需要手動在目標server上改動配置文件,完畢各類配置,僅僅需要固定鍵入例如如下的啓動命令就能夠:markdown
sudo bin/flume-ng agent --conf conf -z 127.0.0.1:2181 -p /component/log/mysqlSlowquery/flume -name mysql-slowquery-30 -Dflume.root.logger=DEBUG,console
而上面這個命令中,惟一需要變更的僅僅有如下幾個部分:網絡
mysql-slowquery-30
事實上原先需要手動改動配置文件的部分參數項將在提供的管控臺中進行配置,但基於web的表單填寫顯然要比在server上以命令行的方式來得easy得多。
這裏咱們的作法是拆解了flume的配置文件,將其固定不變的部分作成模板,將其可變部分作成表單。在提交以前,經過模板引擎將模板跟配置信息進行合併爲完整的配置並推送到Zookeeper中去。
當中需要配置的部分參數有:
相同在以前的文章中我也說起咱們在日誌解析上的選擇是morphline。
morphline是個在Hadoop生態系統中的ETL Framework。morphline也有一個配置文件用於定義一系列的Commands。而它也有固定部分和可變部分,因爲解析主要應用了morphline的Grok
命令,因此針對這個命令,可變部分主要是:
咱們的作法相同類似於日誌採集模塊。將morphline的配置文件的固定部分作成固定模板。而後將可變部分在管控臺上進行配置。終於合併提交到Zookeeper中。
日誌解析服務(歸屬於如下的後臺服務
)。在啓動時會依據本身的日誌類型,從Zookeeper的特定節點下找到該日誌類型的morphline的配置,將其獲取下來並保存在本地文件系統中,而後構建Mrophline對象(因爲morphline眼下僅僅提供基於File對象的構造方式。因此多了一個先保存至本地文件再基於文件構造Morphline對象的步驟)進行解析。
日誌解析這邊僅僅是給解析任務提供了 元數據 。真正的解析由後臺的解析任務來完畢,咱們將類似的這些贊成在後臺的所有任務籠統得歸結爲 後臺服務 。
後臺服務遵循:任務組
->任務
->工做線程
的層次性的組織方式。依照服務的業務類型(說白了就是同一套處理邏輯)。將其劃分爲不一樣的任務組(比方:ETL組、日誌索引組等)。不一樣的任務組下會有 至少 一個任務。比方ETL任務組下就會有很是多個任務(比方:nginx訪問日誌解析任務、mysql慢查詢日誌解析任務等)。而某一個任務下又存在至少一個工做線程。
在管控臺有一個後臺服務模塊。專門用於管理任務對象以及它們的元數據。
運行任務的工做者線程自己是無狀態的。它們在啓動的時候會去Zookeeper上下載它們運行任務所需要的元數據。
一般,爲了服務的可用性咱們會爲每個任務配備很多於一個工做者線程。
固然,這裏的 很多於一個 並不只僅是基於一個運行後臺服務的JVM進程或一個主機節點來計數的,而是針對由多個節點組成的集羣而言。
這經過一個配置來實現:
worker.minimumNumPerTask=2
它的意義是:對每個task而言,啓動的最少的worker線程數。假設一個主機節點上啓動兩個後臺服務的JVM進程,那麼這個task就會相應4個工做者線程。
正常狀況下,每個任務在同一時刻僅僅有一個處於active狀態的工做者線程,而其它搶佔失敗的都會將本身切換爲standby模式。做爲備援隨時待命。
這個機制是怎樣實現的?這得益於Zookeeper提供的 暫時順序 節點。
當多個工做者線程去競爭一個任務的時候,它們首先去該任務的path下建立一個子path,並註冊本身的主機等信息。
注意這裏建立的子path的類型不一樣於其它Zookeeper使用場景的path類型(其它path一般都是持久型的),它是暫時、順序的。這兩個屬性很是重要:
各個工做者線程建立暫時順序的path後,因爲具備 順序 性。Zookeeper會依照它們建立的順序在path後追加帶有從1開始遞增的編號。各個工做者建立完畢後會獲得各自的編號,而後它們做一個順序推斷,誰是最小的誰就會得到任務的運行機會併成爲active工做者,而其它搶佔失敗的將默認切換到standby模式,這些搶佔失敗的工做者線程會註冊成爲它們搶佔task的 子節點變動 watcher。這時 暫時 屬性就派上用場了,當處於active模式的工做者線程丟失會話以後,這些standby將會收到通知,而這時它們會再次去推斷本身的編號是否是最小的。假設是那麼就可以接替以前的工做者線程成爲active的了。這時假設有新加入的工做者線程也會觸發變動通知,但這並不會影響正常的邏輯。
固然這裏還存在一些問題有待無缺:
每個任務組都會有一個watcher來監控是否有新的任務被建立(比方一種新的日誌類型被提交)。假設有新任務則會在其所屬的線程池中新建新的工做者來運行新任務。
眼下暫時這個watcher默認僅僅關注新增任務,而針對任務被移除或者任務的元數據變動,watcher暫時尚未相應的響應機制。這也是興許需要考慮和無缺的部分。
這樣的機制以前已經分別應用於日誌採集
、日誌解析
模塊了,在這裏也是爲了簡化後臺服務啓動時配置的問題。
綜上,整個設計的Zookeeper 拓撲圖大體例如如下:
從上面的分析可以看到。咱們最大程度地將各類可變的參數配置到Zookeeper中。使其成爲串聯起整個分佈式系統的配置中心,而咱們的管控臺某種意義上退化成了「配置系統」——將配置界面化、持久化。
同一時候。咱們也利用了Zookeeper的實時事件Push機制,來進行分佈式協調。