Kafka 是一種分佈式的,基於發佈 / 訂閱的消息系統。最初被 LinkedIn 開發,並在 2011 年初開源,2012 年 10 月從 Apache 孵化器破殼而出,成爲 Apache 的頂級項目。數據庫
Kafka 最初被設計的目的是 LinkedIn 流量和運維數據分析。流量數據包含 PV (Page View) , UV (Unique Visitor) ,搜索數據,詳情頁數據等。在高併發場景對於這些數據的統計並不是實時的,不是簡單的對於數據庫的某個字段數據量 +1 這麼簡單,超大的流量洪峯下並不能由於統計數據將業務主流程阻塞。因此一般會將這些數據記錄在文件或大數據存儲引擎中,而後週期性的進行統計分析。設計模式
Kafka 被愈來愈多的公司青睞主要和他的特性優點有關:緩存
讀
和 寫
數據,而且對於數據進行壓縮保證高吞吐那爲什麼須要使用消息隊列,或者說在什麼場景下 Kafka 更加合適服務器
在大數據,高併發的場景下爲了突破性能瓶頸會對系統進行水平擴展和垂直拆分,將一個複雜的系統拆分多個獨立,純淨的子系統。數據在各個系統之間流轉,可是若是某一個服務處理速度過慢,就會拖累整個鏈路的性能,造成瓶頸下降整個系統的性能,形成「旱的旱死澇的澇死」的局面。架構
舉個簡單例子:在淘寶下單時,交易系統完成扣款,後續會有不少動做:提醒賣家發貨,生成賣家工做流,覈銷優惠券,增長購物積分等等,若是這一步所有寫到交易系統的扣款代碼以後,頗有可能交易系統就會被拖死,下游任何一個環節失敗也會致使扣款回滾,而且若是須要添加一個新的動做須要交易去作大量修改,設計確定是不合理的。實際上交易系統在處理完扣款後會發送一個扣款完成消息,下游接這個消息便可,下游失敗不會影響核心流程失敗,而且各個系統的邊界更加清楚,分層更更加合理。併發
現在的應用程序基本都會涉及到多個系統之間的對接,數據在系統之間經過 RPC 進行傳遞,處理數據的過程失敗就會致使數據丟失,除非數據被持久化到磁盤上。而 Kafka 將全部須要流轉的數據都 持久化到磁盤上
,保證數據不會丟失。另外還有一個很重要的能力就是保留現場便於後續問題排查跟蹤,經歷過系統失敗可是沒法復現的人才會體會到的痛!app
爲了保證磁盤上的數據不會爆炸式瘋漲,Kafka 提供了數據清理,數據壓縮等功能,清除處理完成的歷史數據。運維
在應用的訪問量劇增的狀況下,代碼優化每每沒有直接進行水平擴展來的那麼及時。診斷,分析,方案,優化,驗證 一系列複雜流程讓代碼優化看起來只能是一個從長計議的方案。這時止血的方案只能是降級,限流,擴機器 三板斧。Kafka 的擴展性主要就體如今能熱擴容,不須要修改參數,不須要修改代碼,上機器 -> 註冊服務 就完成了擴容。並不是全部系統都具有這個像 調節音量旋鈕同樣簡單的提升系統性能
的能力 ,這裏會涉及到擴容以前的數據是否會有熱點,新節點對集羣的同步,流量重分配等等一系列複雜流程。分佈式
系統的部分組件失敗不會影響這個系統的運行,消息隊列下降了進程間的耦合度,上游或者下游服務掛掉後不會影響其餘系統的運行,在服務從新在線後可以繼續處理以前未處理的數據,只是會存在必定的延時可是可以保證 最終業務正確性
。ide
強哥:你這瓜保熟嗎?哦不,你這隊列保序嗎? 在大多數場景下,數據處理順序是相當重要的,順序錯亂極可能致使數據結果錯誤。除非這個處理過程是無狀態的,此時消息只是起到事件觸發的做用,觸發下游進行計算。Kafka 能夠保證分區內部有序而不能保證全局有序。
上圖是一個典型的 Kafka 架構圖,左邊爲消息生產者(Producer) ,發送消息到一個特定的主題(Topic),因爲 Kafka 的分佈式設計每一個 Topic 被分紅多個分區,所以發送到每一個 Topic 的消息會被存儲到對應的分區。另外若是 Topic 設置了副本,則每一個分區都會有對應的副本。這些 Topic 被不一樣的消費者(Consumer)訂閱,若是兩個消費者在同一個消費者組,那麼裏面的消費者只能訂閱一個固定的分區。
用上圖的 Topic A 舉例, Producer 1 發送消息到 Topic-A ,消息會在存放在 Broker-2 和 Broker-3 的兩個分區上,而且因爲 Topic-A 開啓了分區備份,因此每一個分區都會由另一個節點 Topic-A' 備份分區數據 。發送到 Broker 的數據會被消費者訂閱,因爲 Consumer-1 和 Consumer-2 在同一個消費者組中,他們只能消費一個固定分區的消息, Consumer-1 只會接收到 Topic-A Partition-1 的消息,Consumer-2 只會接收到 Topic-A Partition-0 的消息。
在 Kafka 集羣中的一個 Kafka Server 就是一個 Broker ,生產者將消息投遞到 Broker ,Broker 保證消息的 持久化,容災,準確性等。同時接受消費者的消息訂閱,向消費者分發消息。通常來講在生產環境一臺 Kafka 服務器就是一個 Broker。
Topic 能夠認爲是用來存儲消息的邏輯概念,可簡單認爲他是一個 信箱
。每條消息發送的時候都須要指定須要發送到哪一個 Topic ,消息被消費的時候也須要指定消費哪一個 Topic 中的消息。
Kafka 爲了提升可擴展性以及吞吐量,Topic 被分紅多個分區 (Partition) ,每一個 Partition 對應一個 Log,Log 是一個邏輯概念, 它會對應服務器上一個文件夾,這個文件夾下存放的是這個 Partition 下全部的消息數據和消息索引 。在面對海量數據的時候,爲了不出現巨大文件出現 I/O 瓶頸,Kafka 又將 Log 分爲多個 Segment 。每一個 Segment 包含 log 文件
和 index 文件
文件命名是以該 Segment 第一條消息的 offset 命名。這樣說下來其實仍是很繞的直接看下面的架構圖,能夠仔細留意一下各個部分的標識和數字再結合這段文字,理解起來應該就很輕鬆了。
另外由於 Kafka 採用順序 I/O,順序 I/O 效率很是高,甚至比隨機寫內存效率更高,這也是 Kafka 高性能的緣由之一。
在生產環境中,咱們通常會開啓 Kafka 消息冗餘特性,每一個 Partition 都有 1 個或多個副本,咱們稱之爲 Replication。當分區只有一個副本的時候,該分區數據只保留了一份。每一個分區副本都會選出一個 Leader , Leader 是全部讀寫請求的 「接口人」
,其他副本均爲 Follower 。Follower 做用有兩個:拉取 Leader 的 Log 數據作 備份
,在 Leader 失敗後做爲候選人 參與 Leader 選舉
。
消息產出的源頭,經過必定的策略推送到 Topic 的各個分區 。這裏所說的推送策略就是消息路由機制,Kafka 內置多種策略可選例如:按照消息 Key ,輪訓等等,甚至用戶能夠寫擴展代碼來自定義路由策略。
消費者(Consumer)
主要工做是從 Broker 拉取消息,進行消費處理。每一個消費者維護本身的消費進度,這樣的設計有諸多好處,好比:每一個消費者進度可以輕鬆的進行區分,而且能夠修改單個消費者的消費位點跳過或者從新消費某些消息,避免了位點信息的集中化管理的單點故障問題。
如今的應用程序大部分爲分佈式的系統,一個應用有幾十臺上百臺服務器,這些服務器上運行着相同的代碼,那麼一個消息過來,每臺服務器都執行一次消費邏輯,豈不是會形成巨大的問題。
因此 Kafka 引入了一個新的概念: 消費者組(Consumer Group)
。咱們能夠將這些應用的服務器都放到同一個消費者組中,而 Kafka 規定一條消息只能被同一個消費者組中的一個消費者消費,這樣就能完美避免分佈式狀況下的重複消費問題了。上面所說的狀況簡單來講是但願實現消息被某臺服務器獨佔,也就是 單播
問題。假如咱們但願這條消息被廣播出去,每臺收到這個消息的服務器都作處理,例如發消息作日誌清理,這種狀況稱爲 廣播
, 那咱們只須要將每一個消費者放到不一樣的消費者組便可。
Kafka 引入消費者組的概念巧妙解決了單播和廣播問題,而沒有區分訂閱類型,經過一種邏輯概念來屏蔽掉多種訂閱實現。
另外在同一個消費者組中的消費者訂閱的分區是肯定的,只有在消費者組中的消費者有變化的時候纔會進行重分配。例如咱們有四個分區,三個消費者,就會出現一個消費者訂閱兩個分區的狀況。而三個分區四個消費者就會出現有消費者處於空閒狀態,形成浪費,因此通常消費者的數量儘可能不要大於 Topic 的分區數。
這是我 2021 年的第一篇博客,年末作回顧的時候才知道去年我過的究竟有多麼糟糕。既沒有輸入也沒有輸出,雖然工做進入一個新的階段,會愈來愈忙,但忙不是拒絕成長的藉口,必須保證每個月一到兩本書的輸入,一到兩週輸出一篇優質文章。 最長的路屬於一顆孤獨的心,與君共勉
下期我會從總體梳理 Kafka 生產者,包括消息發送客戶端,發送端數據緩存從源碼角度看看其中的設計模式,代碼組織技巧。