Kafka 初識

暮良文王發表於2019-06-16

1.Kafka 是什麼?

用一句話概括一下:Apache Kafka 是一款開源的訊息引擎系統。

 


倘若“訊息引擎系統“這個詞對你來說有點陌生的話,那麼“訊息佇列“、“訊息中介軟體”的提法想必你一定是有所耳聞的。不過說實話我更願意使用訊息引擎系統這個稱謂,因為訊息佇列給出了一個很不明確的暗示,彷彿 Kafka 是利用佇列的方式構建的;而訊息中介軟體的提法有過度誇張“中介軟體”之嫌,讓人搞不清楚這個中介軟體到底是做什麼的。

像 Kafka 這一類的系統國外有專屬的名字叫 Messaging System,國內很多文獻將其簡單翻譯成訊息系統。我個人認為並不是很恰當,因為它片面強調了訊息主體的作用,而忽視了這類系統引以為豪的訊息傳遞屬性,就像引擎一樣,具備某種能量轉換傳輸的能力,所以我覺得翻譯成訊息引擎反倒更加貼切。


2.這類系統是做什麼用的?

  先來個官方嚴肅版本的答案。

  根據維基百科的定義:訊息引擎系統是一組規範。企業利用這組規範在不同系統之間傳遞語義準確的訊息,實現鬆耦合的非同步式資料傳遞。
果然是官方定義,有板有眼。

  如果覺得難於理解,那麼可以試試我下面這個民間版:
       系統 A 傳送訊息給訊息引擎系統,系統 B 從訊息引擎系統中讀取 A 傳送的訊息。
  最基礎的訊息引擎就是做這點事的!

  不論是上面哪個版本,它們都提到了兩個重要的事實:

  • 訊息引擎傳輸的物件是訊息;
  • 如何傳輸訊息屬於訊息引擎設計機制的一部分。

 

3.它如何設計訊息(訊息的格式)?

  既然訊息引擎是用於在不同系統之間傳輸訊息的,那麼如何設計待傳輸訊息的格式從來都是一等一的大事。

  試問一條訊息如何做到資訊表達業務語義而無歧義,同時它還要能最大限度地提供可重用性以及通用性?

  一個比較容易想到的是使用已有的一些成熟解決方案,比如使用 CSV、XML 亦或是 JSON;又或者你可能熟知國外大廠開源的一些序列化框架,比如 Google 的 Protocol Buffer 或 Facebook 的 Thrift。這些都是很酷的辦法。那麼現在我告訴你 Kafka 的選擇:它使用的是純二進位制的位元組序列。當然訊息還是結構化的,只是在使用之前都要將其轉換成二進位制的位元組序列。

 

4.傳輸訊息的方式?
  訊息設計出來之後還不夠,訊息引擎系統還要設定具體的傳輸協議,即我用什麼方法把訊息傳輸出去。常見的有兩種方法:

  • 點對點模型:也叫訊息佇列模型。如果拿上面那個“民間版“的定義來說,那麼系統 A 傳送的訊息只能被系統 B 接收,其他任何系統都不能讀取 A 傳送的訊息。日常生活的例子比如電話客服就屬於這種模型:同一個客戶呼入電話只能被一位客服人員處理,第二個客服人員不能為該客戶服務。

 

  • 釋出 / 訂閱模型:與上面不同的是,它有一個主題(Topic)的概念,你可以理解成邏輯語義相近的訊息容器。該模型也有傳送方和接收方,只不過提法不同。傳送方也稱為釋出者(Publisher),接收方稱為訂閱者(Subscriber)。和點對點模型不同的是,這個模型可能存在多個釋出者向相同的主題傳送訊息,而訂閱者也可能存在多個,它們都能接收到相同主題的訊息。生活中的報紙訂閱就是一種典型的釋出 / 訂閱模型。比較酷的是 Kafka 同時支援這兩種訊息引擎模型。

  提到訊息引擎系統,你可能會問 JMS 和它是什麼關係。JMS 是 Java Message Service,它也是支援上面這兩種訊息引擎模型的。嚴格來說它並非傳輸協議而僅僅是一組 API 罷了。不過可能是 JMS 太有名氣以至於很多主流訊息引擎系統都支援 JMS 規範,比如 ActiveMQ、RabbitMQ、IBM 的 WebSphere MQ 和 Apache Kafka。當然 Kafka 並未完全遵照 JMS 規範,相反,它另闢蹊徑,探索出了一條特有的道路。

 

5.為什麼要用用它?
  好了,目前我們僅僅是瞭解了訊息引擎系統是做什麼的以及怎麼做的,但還有個重要的問題是為什麼要使用它。
依舊拿上面“民間版“舉例,我們不禁要問,為什麼系統 A 不能直接傳送訊息給系統 B,中間還要隔一個訊息引擎呢?
答案就是“削峰填谷”。這四個字簡直比訊息引擎本身還要有名氣。

  所謂的“削峰填谷”就是指緩衝上下游瞬時突發流量,使其更平滑。特別是對於那種傳送能力很強的上游系統,如果沒有訊息引擎的保護,“脆弱”的下游系統可能會直接被壓垮導致全鏈路服務“雪崩”。但是,一旦有了訊息引擎,它能夠有效地對抗上游的流量衝擊,真正做到將上游的“峰”填滿到“谷”中,避免了流量的震盪。訊息引擎系統的另一大好處在於傳送方和接收方的鬆耦合,這也在一定程度上簡化了應用的開發,減少了系統間不必要的互動。

  說了這麼多,可能你對“削峰填谷”並沒有太多直觀的感受。舉個例子來說明一下 Kafka 在這中間是怎麼去”抗“峰值流量的吧。比如購買課程,每門課程都有一個專門的訂閱按鈕,點選之後進入到付費頁面。這個簡單的流程中就可能包含多個子服務,比如點選訂閱按鈕會呼叫訂單系統生成對應的訂單,而處理該訂單會依次呼叫下游的多個子系統服務 ,比如呼叫支付寶和微信支付的介面、查詢你的登入資訊、驗證課程資訊等。顯然上游的訂單操作比較簡單,它的 TPS 要遠高於處理訂單的下游服務,因此如果上下游系統直接對接,勢必會出現下游服務無法及時處理上游訂單從而造成訂單堆積的情形。特別是當出現類似於秒殺這樣的業務時,上游訂單流量會瞬時增加,可能出現的結果就是直接壓跨下游子系統服務。
解決此問題的一個常見做法是我們對上游系統進行限速,但這種做法對上游系統而言顯然是不合理的,畢竟問題並不出現在它那裡。所以更常見的辦法是引入像 Kafka 這樣的訊息引擎系統來對抗這種上下游系統 TPS 的錯配以及瞬時峰值流量。

  還是這個例子,當引入了 Kafka 之後。上游訂單服務不再直接與下游子服務進行互動。當新訂單生成後它僅僅是向 Kafka Broker 傳送一條訂單訊息即可。類似地,下游的各個子服務訂閱 Kafka 中的對應主題,並實時從該主題的各自分割槽(Partition)中獲取到訂單訊息進行處理,從而實現了上游訂單服務與下游訂單處理服務的解耦。這樣當出現秒殺業務時,Kafka 能夠將瞬時增加的訂單流量全部以訊息形式儲存在對應的主題中,既不影響上游服務的 TPS,同時也給下游子服務留出了充足的時間去消費它們。這就是 Kafka 這類訊息引擎系統的最大意義所在。

 

  對Kafka的初識就到這裡。謝謝閱讀。