1. 程式人生 > >分散式訊息佇列(Message Queue)系統:kafka掃盲

分散式訊息佇列(Message Queue)系統:kafka掃盲

分散式系統很重要的一個設計原則是鬆耦合,即儘量減少子系統間的依賴。這樣各個子系統可以相互獨立的進行演進,維護,重用等。Message Queue (MQ)是一種很好的解耦手段。要了解MQ在系統整合中的作用,可以看Enterprise Integration Patterns (EIP)這本書或對應的網站。簡單說就是釋出者只管把訊息釋出到MQ中而不管誰會來取,同樣訊息使用者只管只管從MQ取訊息而不管是誰釋出的。這樣釋出者和使用者都不用知道對方的存在。

MQ產品也有很多,開源的也不少。常見的有activeMQ,openMQ,RabbitMQ等。以前也用過MQ系統,而最近在思考SaaS系統中如何使用MQ。所以在網上看看目前有什麼樣的MQ系統具有比較好的擴充套件性,可以支援大規模的資料流的,就發現了kafka。

1. kafka是什麼

kafka是LinkedIn開發並開源的一個分散式MQ系統,現在是Apache的一個孵化專案。在它的主頁描述kafka為一個高吞吐量的分散式(能將訊息分散到不同的節點上)MQ。在這片博文中,作者簡單提到了開發kafka而不選擇已有MQ系統的原因。兩個原因:效能和擴充套件性。這裡做適當解釋。

基本上目前絕大多數(如果不是所有的)MQ系統都是針對企業整合應用設計的,而不是針對大規模Service應用設計的。兩者有什麼區別?

企業整合的基本特點是把企業中現存的本不相干的各種應用進行整合。例如:一個企業可能想把財務系統和倉管系統進行整合,減少部門間結算和流通的成本和時間,並能更好的支援上層決策。但這兩個系統是由不同的廠家做的,不能修改。另外企業整合是一個持續漸進的過程,需求變化非常頻繁。這對MQ系統的要求是要非常靈活,可定製性要求高。所以常見的MQ系統通常都可以通過復炸的xml配置或外掛開發進行定製以適應不同企業的業務流程的需要。他們大多數都能通過配置不同程度的支援EIP中定義一些模式。但設計目標並沒有很重視擴充套件性和效能,因為通常企業級應用的資料流和規模都不會非常大。即使有的比較大,使用高配置的伺服器或做一個簡單幾個節點的叢集就可以滿足了。

大規模的service是指面向公眾的向facebook,google,linkedin和taobao這樣級別或有可能成長到這個級別的應用。相對企業整合來講,這些應用的業務流程相對比較穩定。子系統間整合的業務複雜度也相對較低,因為子系統通常也是經過精心選擇和設計的並能做一定的調整。所以對MQ系統的可定製性及定製的複雜性要求並不高。但由於資料量會非常巨大,不是幾臺Server能滿足的,可能需要幾十甚至幾百臺,且對效能要求較高以降低成本,所以MQ系統需要有很好的擴充套件性。

kafka正是一個滿足SaaS要求的MQ系統,它通過降低MQ系統的複雜度來提高效能和擴充套件性。

2. kafka的設計

kafka的

設計文件詳細說明了它的設計思路。這裡簡單列舉並討論一下。

基本概念

kafka的工作方式和其他MQ基本相同,只是在一些名詞命名上有些不同。為了更好的討論,這裡對這些名詞做簡單解釋。通過這些解釋應該可以大致瞭解kafka MQ的工作方式。

  • Producer (P):就是網kafka發訊息的客戶端
  • Consumer (C):從kafka取訊息的客戶端
  • Topic (T):可以理解為一個佇列
  • Consumer Group (CG):這是kafka用來實現一個topic訊息的廣播(發給所有的consumer)和單播(發給任意一個consumer)的手段。一個topic可以有多個CG。topic的訊息會複製(不是真的複製,是概念上的)到所有的CG,但每個CG只會把訊息發給該CG中的一個consumer。如果需要實現廣播,只要每個consumer有一個獨立的CG就可以了。要實現單播只要所有的consumer在同一個CG。用CG還可以將consumer進行自由的分組而不需要多次傳送訊息到不同的topic。
  • Broker (B):一臺kafka伺服器就是一個broker。一個叢集由多個broker組成。一個broker可以容納多個topic。
  • Partition(P):為了實現擴充套件性,一個非常大的topic可以分佈到多個broker(即伺服器)上。kafka只保證按一個partition中的順序將訊息發給consumer,不保證一個topic的整體(多個partition間)的順序。

可靠性(一致性)

MQ要實現從producer到consumer之間的可靠的訊息傳送和分發。傳統的MQ系統通常都是通過broker和consumer間的確認(ack)機制實現的,並在broker儲存訊息分發的狀態。即使這樣一致性也是很難保證的(參考原文)。kafka的做法是由consumer自己儲存狀態,也不要任何確認。這樣雖然consumer負擔更重,但其實更靈活了。因為不管consumer上任何原因導致需要重新處理訊息,都可以再次從broker獲得。

kafka的producer有一種非同步傳送的操作。這是為提高效能提供的。producer先將訊息放在記憶體中,就返回。這樣呼叫者(應用程式)就不需要等網路傳輸結束就可以繼續了。記憶體中的訊息會在後臺批量的傳送到broker。由於訊息會在記憶體呆一段時間,這段時間是有訊息丟失的風險的。所以使用該操作時需要仔細評估這一點。

另外,在最新的版本中,還實現了broker間的訊息複製機制,去除了broker的單點故障(SPOF)。

擴充套件性

kafka使用zookeeper來實現動態的叢集擴充套件,不需要更改客戶端(producer和consumer)的配置。broker會在zookeeper註冊並保持相關的元資料(topic,partition資訊等)更新。而客戶端會在zookeeper上註冊相關的watcher。一旦zookeeper發生變化,客戶端能及時感知並作出相應調整。這樣就保證了新增或去除broker時,各broker間仍能自動實現負載均衡。

負載均衡

負載均衡可以分為兩個部分:producer發訊息的負載均衡和consumer讀訊息的負載均衡。

producer有一個到當前所有broker的連線池,當一個訊息需要傳送時,需要決定發到哪個broker(即partition)。這是由partitioner實現的,partitioner是由應用程式實現的。應用程式可以實現任意的分割槽機制。要實現均衡的負載均衡同時考慮到訊息順序的問題(只有一個partition/broker上的訊息能保證按順序投遞),partitioner的實現並不容易。個人認為這一點還有待改進。

consumer讀取訊息時,除了考慮當前的broker情況外,還要考慮其他consumer的情況,才能決定從哪個partition讀取訊息。具體的機制還不是很清楚,需要做更深入的研究。

效能

效能是kafka設計重點考慮的因素。使用多種方法來保證穩定的O(1)效能。

kafka使用磁碟檔案儲存收到的訊息。它使用一種類似於WAL(write ahead log)的機制來實現對磁碟的順序讀寫,然後再定時的將訊息批量寫入磁碟。訊息的讀取基本也是順序的。這正符合MQ的順序讀取和追加寫特性。

另外,kafka通過批量訊息傳輸來減少網路傳輸,並使用java中的sendfile和0拷貝機制減少從讀取檔案到傳送訊息間記憶體資料拷貝和核心使用者態切換的次數。

根據kafka的效能測試報告,它的效能基本達到了O(1)的複雜度。

3. 總結

從以上來看,個人覺得kafka比較適合用來做簡單的訊息傳遞和分發,能支援大資料量。但如果需要實現複雜的EIP模式,則不像傳統MQ那麼容易。而且,因為只有partition內的訊息才能保證傳遞順序,如果訊息的順序很重要,又需要很好的擴充套件性,使用kafka實現可能會比較困難。所以,kafka應該比較適合處理簡單的事件和訊息,例如資料(log)收集,大量事實資料的實時分析(kafka可與MapReduce整合)。

但需要注意的是,kafka現在還只是Apache的孵化專案,還不是很成熟,雖然開發活動還是比較活躍的。