續 Day 28
老樣子,消費者任何時間都有可能故障,有可能發生 訊息代理 (message broker) 傳遞訊息給消費者後,它卻沒有處理或只處理到一半,為了確保訊息不遺失,broker 使用 acknowledgments:當消費者在處理完訊息後,必須明確的通知 broker,如此 broker 就可以將訊息從 queue 中刪除。
消費者因故障消失時,broker 會將未 acknowledgments 的訊息傳遞給其他消費者,此時若你的傳遞策略是 load balance 時,會發生如下圖 11-2 的影響:訊息 m3 和 m4 傳遞的順序跟事件發生順序不同。
Consumer 2 在處理 m3 到一半時故障,同時 Consumer 1 正在處理 m4, 非 ack 的訊息 m3 會再傳遞給 Consumer 1,其消費訊息的順序為 m4, m3, m5,與發生順序不同。
如果訊息彼此間有因果關係,避免此問題的方法就是為每個消費者使用單獨的 queue,例如不要用 load balance 傳遞策略。
如果你是使用 AMQP/JMS 風格的訊息模型,當訊息被 acknowledgments 後會從 broker 中刪除,因為它們是以訊息傳遞的思維來建構,如果你在此系統中註冊新增一個消費者,它無法接收註冊前的訊息,相較於資料庫或檔案系統來說就沒這問題,因此,一個混合有耐用性儲存及低延遲訊息通知系統就誕生了:基於 log 的訊息代理 (log-based message broker)。
我們在之前討論過 Log-structured 儲存引擎 (2020 Day 8) 和 數據複製 (2020 Day 21),我們可以用相同的架構實作訊息代理:生產者的訊息會附加在 log 中,消費者透過讀取 log 來接收訊息,當消費者讀完訊息了,就後待新訊息附加至 log 後的通知。這就像 Unix 工具的 tail -f
一樣,當檔案被寫入後你會同步看到內容一樣。
這裡 log 也能使用 partition (2020 Day 27) 來應付更高的吞吐量,不同分區由不同的節點負責,每一個分區都是獨立的讀取和寫入,一個主題可以被定義為一組分區,這種方法如下圖 11-3 所示:
每一個分區,broker 會分配一個逐漸累加的序列號到每一條訊息上,稱為 offset,就是下圖方框內的數字,因為 log 只能附加上去,所以每一個分區的訊息皆是完全排序,但不同分區的順序無法保證。
Apache Kafka、Amazon Kinesis Streams 和 Twitter DistributedLog 皆是基於 log 的訊息代理。
offset 讓消費訊息的追蹤更簡單了,比較一下各訊息的 offset 跟消費者當前的 offset 大小就好,也因此 broker 就不用在花費力氣追蹤各訊息的 acknowledgments 了,它只要定期記錄消費者的 offset 就好,進而提高訊息代理的吞吐量。