解決RabbitMQ訊息丟失與重複消費問題
1. 背景
最近使用者反饋提交的SQL
查詢一直處於長時間等待狀態,經過排查觀察,發現部分查詢請求丟失,導致使用者提交的查詢未被正常接收,繼而長時間無響應。
現象:即使SQL
控制檯提交10個簡單SQL
查詢 -> 訊息傳送方:傳送10條訊息至訊息佇列 -> 訊息消費方:只消費了7條訊息
2. 現狀
2.1. 當前SQL查詢的整體流程
- 生產者:
PHP
:
- 將使用者的SQL查詢記錄在DB表,標識查詢任務狀態(
f_status
)為執行中; - 將DB表中的任務id、提交人等資訊傳送到
RabbitMQ
;
- 將使用者的SQL查詢記錄在DB表,標識查詢任務狀態(
- 訊息佇列:
RabbitMQ
:
PHP
訊息提交到了交換機;- 交換機再把訊息分發給指定的訊息佇列;
- 消費者:
Python
:
- 主程式監聽訊息佇列,一旦有訊息就不停拉取;
- 拉取一條訊息,就從程式池調起一個空閒程式來處理訊息;
- 隨後反饋ACK給訊息佇列,將訊息從訊息佇列中移除;
2.2. 訊息傳送方:Web端
結論:訊息傳送正常
排查步驟:檢視log
2.3. 訊息佇列
結論:訊息數量正常
診斷步驟:
執行機安裝rabbitmq-dump-queue
外掛,用於dump
佇列的訊息;
1. 執行機:停止服務;
2. 使用者:提交10個SQL
查詢:
3. 傳送方:檢視Web
服務端的輸出日誌,確定10個訊息已經往訊息佇列寫;
4. 執行機:通過rabbitmq-dump-queue
檢視佇列的訊息,確認是正常10個訊息寫入;
watch -n 1 '$GOPATH/src/rabbitmq-dump-queue/rabbitmq-dump-queue -uri="amqp://guest:guest@xxxxx:5672" -queue ph_open_task'
5. 執行機:啟動服務,訊息佇列中的訊息全部被接收;
2.4. 訊息接收方
程式碼邏輯:
try:
pool = Pool(processes=40)
def callback(ch, method, properties, body):
try:
doSomething...
pool.apply_async(process)
except Exception as e:
print traceback.format_exc()
logger_msg.info(traceback.format_exc())
finally:
// 這裡會有問題,即使訊息未被處理也會反饋ACK給RabbitMQ
ch.basic_ack(delivery_tag=method.delivery_tag)
while True:
try:
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='xxxxxxxx'))
channel = connection.channel()
channel.queue_declare(queue=queue_name, durable=True)
channel.basic_qos(prefetch_count=1)
channel.basic_consume(callback, queue=queue_name, no_ack=False)
channel.start_consuming()
except pika.exceptions.ConnectionClosed as e:
continue
except Exception as e:
logger_msg.info(traceback.format_exc())
finally:
channel.basic_ack(delivery_tag=method.delivery_tag)
pool.close()
pool.join()
結論:本例中消費者主程式將持續監聽MQ
,一旦MQ
有訊息將會拉取,隨後從程式池中啟動子程式來處理訊息,但是從程式池啟動子程式的過程並不一定成功(若當前程式池沒有空閒子程式),而主程式不管任何情況下都給MQ
傳送ACK
狀態碼,從而MQ
將未處理的訊息移除掉,導致訊息丟失
3. 方案
問題是在消費者環節產生,因此對消費者做改動,需要調整消費者的架構:
- 原來邏輯:使用程式池技術,主程式負責監聽、接收
MQ
的訊息,子程式負責執行MQ
的訊息,缺點是單一的主程式無法簡單處理ACK
狀態碼,不易維護; - 現有邏輯:使用
RabbitMQ
自身特性(work_queue
),消費者不再維護程式池,是單程式,負責監聽、接收、處理MQ的訊息
,處理完了以後再反饋ACK
狀態碼,程式與程式之間互不干擾,易維護,併發量大時可隨時增加消費者程式;
目前方案的問題以及解決方案:
- 問題1:訊息重複消費
描述:使用者在頁面停止查詢時,會導致消費者程式被殺死,因此ACK
狀態碼未反饋至MQ,從而訊息一直存留在MQ
中,當新的消費者啟動時會重新消費;
解決方案:消費者每次執行查詢前,首先在DB上查詢任務的執行狀態,若處於「取消/失敗/成功」則表示已經由其它消費者消費過,那麼直接返回ACK
狀態碼給MQ
,將訊息從MQ
中移除; - 問題2:程式池如何維護?
描述:使用者在頁面停止查詢時,會導致消費者程式被殺死,導致消費者數量減少;
解決方案:維護一個監控指令碼,每分鐘輪詢消費者程式數,若少於40個程式,則新啟動一個消費者,直到數量足夠;
相關文章
- RabbitMQ如何解決被重複消費和資料丟失的問題?MQ
- mq要如何處理訊息丟失、重複消費?MQ
- 實際業務處理 Kafka 訊息丟失、重複消費和順序消費的問題Kafka
- 《RabbitMQ》如何保證訊息不被重複消費MQ
- RocketMq訊息丟失問題解決MQ
- RabbitMQ:訊息丟失 | 訊息重複 | 訊息積壓的原因+解決方案+網上學不到的使用心得MQ
- 如何處理RabbitMQ 訊息堆積和訊息丟失問題MQ
- RabbitMQ防止訊息丟失MQ
- Kafka重複消費和丟失資料研究Kafka
- kafka9重複消費問題解決Kafka
- 解決alertmanager重複傳送訊息的問題
- Kafka 訊息丟失與消費精確一次性Kafka
- kafka 消費組功能驗證以及消費者資料重複資料丟失問題說明 3Kafka
- 《RabbitMQ》 | 訊息丟失也就這麼回事MQ
- RabbitMq如何確保訊息不丟失MQ
- RocketMQ訊息丟失解決方案:事務訊息MQ
- 訊息中介軟體—RocketMQ訊息消費(三)(訊息消費重試)MQ
- 使用SpringCloud Stream結合rabbitMQ實現訊息消費失敗重發機制SpringGCCloudMQ
- 阿里面試題剖析,如何保證訊息不被重複消費?阿里面試題
- RabbitMQ-如何保證訊息不丟失MQ
- 透過 Pulsar 原始碼徹底解決重複消費問題原始碼
- 分散式訊息佇列:如何保證訊息不被重複消費?(訊息佇列消費的冪等性)分散式佇列
- 訊息佇列-如何保證訊息的不被重複消費(如何保證訊息消費的冪等性)佇列
- RabbitMQ,RocketMQ,Kafka 事務性,訊息丟失和訊息重複傳送的處理策略MQKafka
- RabbitMQ多消費者順序性消費訊息實現MQ
- 《RabbitMQ》| 解決訊息延遲和堆積問題MQ
- Spring Cloud Stream如何處理訊息重複消費?SpringCloud
- RabbitMQ訊息佇列入門及解決常見問題MQ佇列
- Rabbitmq消費者冪等性(不重複消費)MQ
- 探索RocketMQ的重複消費和亂序問題MQ
- “田由甲” - Kafka重複消費線上問題暴雷Kafka
- kafka 如何保證不重複消費又不丟失資料?Kafka
- RocketMQ訊息丟失解決方案:同步刷盤+手動提交MQ
- kafka消費者消費訊息的流程Kafka
- 訊息中介軟體消費到的訊息處理失敗怎麼辦?
- hyperf框架使用rabbitMQ生產訊息至laravel/lumen進行消費框架MQLaravel
- JavaScript中解決計算精度丟失的問題JavaScript
- SpringCloud解決feign呼叫token丟失問題SpringGCCloud