高可用延迟队列设计与实现
延遲隊列:一種帶有?延遲功能?的消息隊列
這樣解釋,整個設計就清楚了。你的目的是 延時,承載容器是 mq。
背景
列舉一下我日常業務中可能存在的場景:
為了解決以上問題,最簡單直接的辦法就是定時去掃表:
服務啟動時,開啟一個異步協程 → 定時掃描 msg table,到了事件觸發事件,調用對應的 handler
幾個缺點:
最大問題其實是什么?
調度模型基本統一,不要做重復的業務邏輯
我們可以考慮將邏輯從具體的業務邏輯里面抽出來,變成一個公共的部分。
而這個調度模型,就是?延時隊列?。
其實說白了:
延時隊列模型,就是將未來執行的事件提前存儲好,然后不斷掃描這個存儲,觸發執行時間則執行對應的任務邏輯。
那么開源界是否已有現成的方案呢?答案是肯定的。Beanstalk (https://github.com/beanstalkd/beanstalkd) 它基本上已經滿足以上需求
設計目的
依次說說上述這些目的的設計方向:
消費行為
這個概念取自 mq 。mq 中提供了消費投遞的幾個方向:
- at most once?→ 至多一次,消息可能會丟,但不會重復
- at least once?→ 至少一次,消息肯定不會丟失,但可能重復
- exactly once?→ 有且只有一次,消息不丟失不重復,且只消費一次。
exactly once?盡可能是 producer + consumer 兩端都保證。當 producer 沒辦法保證是,那 consumer 需要在消費前做一個去重,達到消費過一次不會重復消費,這個在延遲隊列內部直接保證。
最簡單:使用 redis 的 setNX 達到 job id 的唯一消費
高可用
支持多實例部署。掛掉一個實例后,還有后備實例繼續提供服務。
這個對外提供的 API 使用 cluster 模型,內部將多個 node 封裝起來,多個 node 之間冗余存儲。
為什么不使用 kafka?
考慮過類似基于 kafka/rocketmq 等消息隊列作為存儲的方案,最后從存儲設計模型放棄了這類選擇。
舉個例子,假設以 Kafka 這種消息隊列存儲來實現延時功能,每個隊列的時間都需要創建一個單獨的 topic(如: Q1-1s, Q1-2s..)。這種設計在延時時間比較固定的場景下問題不太大,但如果是延時時間變化比較大會導致 topic 數目過多,會把磁盤從順序讀寫會變成隨機讀寫從導致性能衰減,同時也會帶來其他類似重啟或者恢復時間過長的問題。
架構設計
API 設計
producer
consumer
使用延時隊列后,服務整體結構如下,以及隊列中 job 的狀態變遷:
生產實踐
主要介紹一下在日常開發,我們使用到延時隊列的哪些具體功能。
生產端
消費端
首先,框架層面保證了消費行為的?exactly once?,但是上層業務邏輯消費失敗或者是出現網絡問題,亦或者是各種各樣的問題,導致消費失敗,兜底交給業務開發做。這樣做的原因:
這里描述一下框架消費端是怎么保證消費行為的統一:
分為 cluster 和 node。cluster:
https://github.com/tal-tech/go-queue/blob/master/dq/consumer.go#L45
node:
go-queue/consumernode.go at master · tal-tech/go-queue · GitHub
所以對于消費端,開發者需要自己實現消費的冪等性。
總結
以上是生活随笔為你收集整理的高可用延迟队列设计与实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ECMAScript 2021(ES12
- 下一篇: Java进阶高级程序员必备:深入浅出sp