1. 可擴展的事件驅動處理 1.1. 使用消息傳遞系統進行通信,你可以創建松耦合的架構 1.1.1. 消息生產者只是將消息存儲在隊列中,而不用關心消費者如何處理消息 1.1.2. 有一個或多個消費者,並且生產者和消費者的集合可以隨著時間的推移而改變 1.1.3. 有助於提高服務響應能力、通過緩存消除 ...
1. 可擴展的事件驅動處理
1.1. 使用消息傳遞系統進行通信,你可以創建松耦合的架構
-
1.1.1. 消息生產者只是將消息存儲在隊列中,而不用關心消費者如何處理消息
-
1.1.2. 有一個或多個消費者,並且生產者和消費者的集合可以隨著時間的推移而改變
-
1.1.3. 有助於提高服務響應能力、通過緩存消除請求到達峰值以及在面對不可用的消費者時保持系統處理能力
-
1.1.4. 傳統上,用於實現非同步系統的消息代理技術側重於消息傳輸
-
1.1.5. RabbitMQ或ActiveMQ等代理平臺支持隊列集合用於基於FIFO的記憶體或基於磁碟的臨時存儲
-
1.1.6. 當消費者訪問隊列中的消息時,該消息將從代理中刪除
-
1.1.6.1. 稱為破壞性消費者語義
-
1.1.6.2. 如果使用發佈-訂閱消息傳遞,代理會實現機制來維護隊列中的每條消息,直到所有活動訂閱者都消費了這條消息,新的訂閱者看不到舊的消息
-
1.1.6.3. 釋放了代理資源,但也破壞了事件的任何顯式記錄
-
1.1.7. 代理通常還實現一些額外功能用於消息過濾和路由
1.2. 從事件驅動架構的視角重新審視非同步系統
- 1.2.1. Kafka的設計旨在支持大規模的事件驅動系統,利用簡單的持久化消息日誌數據結構和非破壞性消費者語義
1.3. 事件驅動的架構適用於現代業務環境中的許多用例
-
1.3.1. 可以使用事件來捕獲外部活動,並將其流式傳輸到分析系統中,實時洞察用戶和系統的行為
-
1.3.2. 可以使用事件來描述所發佈狀態的變更,以支持跨不同系統或多個微服務的集成
1.4. 事件處理系統需要一個可靠、健壯和可擴展的平臺來捕獲和傳遞事件
1.5. Kafka將事件持久化在主題中,這些主題由消費者以非破壞性的方式處理
- 1.5.1. 可以對主題進行分區和複製,以提供更好的可擴展性和可用性
2. 事件驅動架構
2.1. 事件表示在應用程式的上下文場景中發生了一些有趣的事情
-
2.1.1. 系統捕獲到的外部事件
-
2.1.2. 由於某些狀態變更而在內部生成的事件
2.2. 事件通常會發佈給消息傳遞系統
-
2.2.1. 事件源只是發出事件,並不關心系統中的其他組件如何處理這些事件
-
2.2.2. 重要的是事件源不會註意到事件生成所觸發的操作
-
2.2.3. 產生的架構是松耦合的,併為合併新的事件消費者提供了高度的靈活性
2.3. 事實證明,在簡單的日誌數據結構中保存一份不可變事件的永久記錄是非常有用的
-
2.3.1. 與大多數消息代理管理的FIFO隊列相比,事件日誌是一種只允許追加的數據結構
-
2.3.2. 記錄附加到日誌的末尾,每個日誌條目都有一個唯一的條目號
-
2.3.3. 條目號表示捕獲系統中事件的順序
-
2.3.4. 具有較低序號的事件被定義為發生在具有較高序號的事件之前
-
2.3.5. 此順序在分散式系統中特別有用,可以用來分析和洞察應用程式的行為
2.4. 日誌是關於每個包裹在任意時刻(曾經)所在位置的唯一真實來源
2.5. 基於事件的系統的另一個常見用例是保持複製數據在微服務之間同步
- 2.5.1. 事件日誌本質上是用於跨微服務的數據複製,以便實現狀態傳遞
2.6. 事件日誌的持久性的關鍵優勢
-
2.6.1. 可以隨時引入新的事件消費者
-
2.6.1.1. 日誌存儲的是永久的、不可變的事件記錄,新的消費者可以訪問完整的事件歷史記錄
-
2.6.1.2. 現有的和新的事件都可以處理
-
2.6.2. 可以修改現有的事件處理邏輯,以添加新功能或修複錯誤
-
2.6.2.1. 可以在完整的日誌上執行新邏輯以豐富結果或修複錯誤
-
2.6.3. 如果發生伺服器或磁碟故障,你可以從日誌中恢復最後已知的狀態並重播事件以恢複數據集
-
2.6.3.1. 類似於事務日誌在資料庫系統中的作用
2.7. 缺點
-
2.7.1. 從日誌中刪除事件
-
2.7.1.1. 有一些用例需要刪除日誌條目
-
2.7.1.2. 僅允許追加的不可變日誌不是為刪除條目而設計的,可能會使刪除條目出現問題
2.8. 兩種主要的日誌條目刪除機制
-
2.8.1. 生存時間
-
2.8.1.1. 在預設的兩周後刪除日誌條目
-
2.8.1.2. 可以調整它來滿足你對日誌條目保留和刪除的要求
-
2.8.2. 壓縮主題
-
2.8.2.1. 主題可以配置為僅保留給定事件鍵的最新條目
-
2.8.2.2. Kafka會將較舊的條目標記為刪除
-
2.8.2.3. 事件實際上在壓縮主題中被標記為刪除,併在稍後某個時間段(日誌壓縮任務運行時)被刪除
-
2.8.2.4. 此任務的頻率是可配置的
3. Apache Kafka
3.1. Kafka的核心是一個分散式的持久日誌存儲
-
3.1.1. Kafka起源於LinkedIn,旨在簡化其系統集成的工作
-
3.1.2. 2012年被轉化為一個Apache項目
-
3.1.3. 笨代理/聰明消費者的架構
-
3.1.3.1. 產生的架構已被證明具有令人難以置信的可擴展性,並能提供非常高的吞吐量
-
3.1.4. 日誌條目被持久存儲,可以被多個消費者多次讀取
-
3.1.4.1. 消費者只需指定他們希望讀取條目的日誌偏移量或索引
-
3.1.5. 代理的主要功能是有效地將新事件追加到持久日誌、將事件傳遞給消費者以及管理日誌分區和複製來實現可擴展性和可用性
-
3.1.5.1. 代理從維護與消費者相關的複雜狀態中解放出來
3.2. Kafka連接
-
3.2.1. 一個設計用於構建連接器以將外部數據系統鏈接到Kafka代理的框架
-
3.2.2. 使用該框架構建高性能連接器,從你自己的系統生成或消費Kafka消息
3.3. Kafka流
-
3.3.1. 一個輕量級的客戶端庫
-
3.3.2. 用於從存儲在Kafka代理中的事件構建流應用程式
-
3.3.3. 數據流表示無限的、不斷更新的數據集
-
3.3.4. 流應用程式通過處理批量或某時間視窗的數據來提供有用的實時洞察力
-
3.3.5. Kafka支持高度分散式集群部署,其中由代理通信來分發和複製事件日誌
3.4. Kafka將元數據管理委托給Apache ZooKeeper
-
3.4.1. 元數據實質上指定了多個事件日誌在集群中的位置,以及集群狀態的各種其他元素
-
3.4.2. ZooKeeper是一種高可用性服務,許多分散式平臺用它來管理配置信息和支持組協調(group coordination)
-
3.4.3. ZooKeeper提供了一個類似於普通文件系統的分層命名空間,Kafka用此來在外部維護集群狀態,使其對所有代理可用
-
3.4.4. 意味著你必須創建一個ZooKeeper集群(為了可用性)並使Kafka集群中的代理可以訪問它
-
3.4.5. Kafka對ZooKeeper的使用對你的應用程式來說是透明的
3.5. 主題
-
3.5.1. Kafka的主題相當於一般消息傳遞技術中的隊列
-
3.5.2. 主題由代理管理,並且始終是持久的
-
3.5.3. 主題由僅允許追加的日誌實現,這意味著新事件總是寫入日誌的尾部
-
3.5.3.1. 從主題中讀取事件是非破壞性的
-
3.5.3.2. 每個主題都會保留所有事件,直到特定主題所配置的事件保留時間到期
-
3.5.3.3. 當事件的存儲時間超過此保留時間時,它們會自動從主題中刪除
-
3.5.4. 消費者通過指定其希望訪問的主題名稱以及其想要讀取的消息的索引或偏移量來讀取事件
-
3.5.5. 代理利用日誌的僅追加特性來充分發揮磁碟的線性讀寫性能
-
3.5.5.1. 操作系統針對這些數據訪問模式進行了大量優化,使用數據預取和緩存等技術,使Kafka能夠提供恆定的訪問時間,無論主題中存儲的事件數量如何
3.6. 生產者和消費者
-
3.6.1. Kafka為生產者提供API來寫入事件,同時為消費者提供API來從主題中讀取事件
-
3.6.2. 一個事件擁有一個應用程式定義的鍵和一個相關聯的值,以及一個發佈者提供的時間戳
-
3.6.3. 批量累積事件可以減少Kafka傳遞事件過程中到代理的網路往返次數
-
3.6.3.1. 使代理在將事件批次追加到主題時執行更少次數、更大量的寫入
-
3.6.3.2. 共同促成Kafka系統實現高吞吐量的大部分因素
-
3.6.4. 生產者上的緩衝事件允許你權衡為提升系統吞吐量而批量累積事件時所產生的額外延遲(linger.ms值)
-
3.6.5. Kafka通過acks配置參數為生產者提供不同的事件傳遞確認機制
-
3.6.5.1. 值為0表示不提供傳遞確認,這是一個“即發即棄”的選項——事件可能會丟失
-
3.6.5.2. 值為1意味著一旦事件被持久化到目標主題,代理就會確認該事件
-
3.6.5.3. 短暫的網路故障可能會導致生產者重試失敗的事件,從而導致重覆
> 3.6.5.3.1. 如果不能接受重覆事件,可以將enable-idempotence配置參數設置為true
> 3.6.5.3.2. 此設置讓代理過濾掉重覆事件並提供嚴格一次(exactly-once)的傳遞機制
-
3.6.6. Kafka消費者利用拉取模型從主題中批量檢索事件
-
3.6.6.1. 如果消費者在批處理事件時失敗,則這批事件將不會重新投遞
-
3.6.6.2. 在批處理完所有事件後調用commitSync(),為消費者提供至少一次投遞的保證
-
3.6.6.3. 如果消費者在處理一批事件時崩潰,則不會提交偏移量,當消費者重新啟動時,事件將被重新投遞
-
3.6.7. Kafka的消費者API不是線程安全的
-
3.6.7.1. 與代理的所有網路交互都發生在檢索事件的同一客戶端線程中
-
3.6.7.2. 要併發處理事件,消費者需要自己實現線程方案
-
3.6.7.3. 一種常見的方法是每個消費者一個線程(thread-per-consumer)的模型,它提供了一個簡單的解決方案,代價是要在代理端管理更多的TCP連接和獲取請求
-
3.6.7.4. 另一種選擇是使用單個線程獲取事件,並將事件處理放到處理線程池中
> 3.6.7.4.1. 這可能會提供更大的可擴展性,但會使手動提交事件變得更複雜,因為線程需要以某種方式進行協調,以確保在提交之前主題的所有事件都已處理完成
3.7. 可擴展性
-
3.7.1. 可擴展性機制是主題分區
-
3.7.2. 當創建一個主題時,要指定存儲事件需要使用的分區數量,Kafka將分區分佈在集群中的代理上
-
3.7.3. 生產者和消費者可以分別並行地寫入和讀取不同的分區,提供了水平可擴展性
-
3.7.4. 根據Kafka實現的“笨代理”架構,由生產者而不是代理負責選擇將事件分配到哪個分區
-
3.7.4.1. 使得代理能夠專註於接收、存儲和傳遞事件等主要目的
-
3.7.5. 當指定事件的鍵時,分區程式會使用鍵值的哈希函數來選擇分區
-
3.7.5.1. 會將具有相同鍵值的事件定向到相同的分區,這對於聚合處理事件的消費者來說非常有用
-
3.7.6. 對主題進行分區會影響事件的排序
-
3.7.6.1. 會按照生產者生成事件的順序將事件寫入單個分區,事件將按照寫入的順序從分區中消費
-
3.7.6.2. 意味著每個分區中的事件都是按時間來排序的,並且提供事件流的部分排序
-
3.7.6.3. 分區之間沒有事件的總順序
-
3.7.7. 分區還可以將事件併發傳遞給多個消費者
-
3.7.7.1. Kafka為一個主題引入了消費者組的概念
-
3.7.7.2. 一個主題的消費者組可以包含一個或多個消費者,最多可達到為主題配置的分區數
-
3.7.7.3. 如果組內的消費者數等於分區數,則Kafka會將組中的每個消費者分配到一個分區
-
3.7.7.4. 如果組內的消費者數小於分區數,則部分消費者會被分配來自多個分區的消息
-
3.7.7.5. 如果組內的消費者數大於分區數,部分消費者將不會被分配到分區並保持空閑狀態
-
3.7.7.6. 為了實現重新平衡,Kafka會從消費者組中選擇一個消費者作為組長
-
3.7.7.7. 對於未在消費者之間移動的分區,事件處理可以繼續進行,不會停機
-
3.7.7.8. 只需添加分配給消費者的新分區
-
3.7.7.9. 對於任意未出現在新分配中的現有消費者分區,消費者完成當前批次消息的處理,提交偏移量,並放棄其訂閱
-
3.7.7.10. 一旦消費者放棄訂閱,該分區便會被標記為未分配
3.8. 可用性
-
3.8.1. 在Kafka中創建主題時,可以指定一個複製因數N
-
3.8.1.1. N會促使Kafka使用領導者-追隨者架構將主題中的每個分區複製N次
-
3.8.2. 如果領導者發生故障,Kafka可以自動進行故障轉移,切換到其中一個追隨者,以確保分區保持可用
4. 案例
4.1. Kafka作為底層消息傳遞組件,被廣泛部署在跨多個垂直業務的事件處理系統中
4.2. Big Fish Games是領先的消費型游戲製作商
-
4.2.1. 使用Kafka來捕獲游戲運行所產生的高吞吐量事件
-
4.2.1.1. 游戲遙測
-
4.2.1.2. 捕獲的數據包括各種事件,例如游戲設備和會話信息,應用內購買和對營銷活動的響應,以及特定於游戲的事件
-
4.2.1.3. 該事件流被輸入一系列下游分析應用中,為Big Fish提供對游戲功能使用和用戶行為模式的實時監測
4.3. Slack利用Kafka從其Web客戶端捕獲那些因處理成本太高而無法同步處理的事件