在使用消息隊列時,可能會遇到以下三個問題:
一.消息丟失
產生的原因:消息發送出去,由于網絡問題或系統異常沒有抵達服務器;
解決辦法:
- 做好容錯方法(try-catch),發送消息可能會網絡失敗,失敗后要有重試機 制,可記錄到數據庫,采用定期掃描重發的方式
- 做好日志記錄:每個消息狀態是否都被服務器收到都應該記錄
- 做好定期重發:如果消息沒有發送成功,定期去數據庫掃描未成功的消息進 行重發
- 消息抵達Broker:Broker要將消息寫入磁盤(持久化)才算成功。此時Broker尚 未持久化完成,宕機。
- publisher也必須加入確認回調機制,確認成功的消息,修改數據庫消息狀態。
- 自動ACK的狀態下。消費者收到消息,但沒來得及消息然后宕機
- 一定開啟手動ACK,消費成功才移除,失敗或者沒來得及處理就noAck并重 新入隊。
概括起來就是:1.做好消息確認機制,包括生產者和消費者兩端(public,consumer【手動ack】);2.每一個發送的消息都在數據庫做好記錄,定期將失敗的消息再次發送一次。
附日志記錄表建表SQL:
CREATE TABLE `mq_message` (`message_id` char(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,`content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,`routing_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`class_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,`message_status` int(4) NULL DEFAULT 0 COMMENT '0-新建 1-已發送 2-錯誤抵達 3-已抵達',`create_time` datetime NULL DEFAULT NULL,`update_time` datetime NULL DEFAULT NULL,PRIMARY KEY (`message_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
二.重復消費
產生的原因:
- 消息消費成功,事務已經提交,ack時,機器宕機。導致沒有ack成功,Broker的消息 重新由unack變為ready,并發送給其他消費者;
- 消息消費失敗,由于重試機制,自動又將消息發送出去;
- 成功消費,ack時宕機,消息由unack變為ready,Broker又重新發送。
解決辦法:
- 消費者的業務消費接口應該設計為冪等性的。比如扣庫存有 工作單的狀態標志
- 使用防重表(redis/mysql),發送消息每一個都有業務的唯 一標識,處理過就不用處理
- rabbitMQ的每一個消息都有redelivered字段,可以獲取是否 是被重新投遞過來的,而不是第一次投遞過來的。
冪等性通俗來說,就是消息在進行處理時,判斷數據的狀態,如果是已處理過的則不再進行處理。
三.消息積壓
產生的原因:
- 消費者宕機積壓;
- 消費者消費能力不足積壓;
- 發送者發送流量太大。
解決辦法:
- 上線更多的消費者,進行正常消費
- 上線專門的隊列消費服務,將消息先批量取出來,記錄數據庫,離線慢慢處理。