如果架構中有用到mq,那就不可避免會遇到消息堆積的問題,因爲我們沒辦法保證自己生產和消費永遠都是正確的。像我們系統就遇到過很多次消息堆積情況,最嚴重的一次直接導致mq內存溢出,服務宕機,導致所有的mq消費全部出現異常,下面我就這個問題和童靴們嘮叨嘮叨。
消息推送校驗模式:
遇到這個問題,第一個想法就是在推送消息的地方做改動,比如要推送mq的時候,先檢查一下mq對應的隊列是否達到上限,如果達到就不推送。
但是如果消息具有時效性,也就是最新推送的消息和mq中已經推送的消息,是不一樣的,這個時候就不能這樣處理,而且如果推送的時候正好mq不穩定,導致獲取隊列消息失敗,就可能導致錯誤的操作,所以這種方案可用性太低。
監聽器消費模式:
後面甚至還想通過監聽器來消費掉這些堆積的消息(該監聽器只用來ack掉消息,不做任何業務處理),但是這樣不僅影響服務器的性能還影響網絡帶寬,所以這種方式也是不可取的。
腳本後臺清理模式:
最終確定下來的方案是通過腳本來刪除,因爲RabbitMq支持命令查詢、修改、清空隊列,基於這種方式,我們可以寫一個腳本,定期獲取需要監控的隊列數據情況,如果達到上限,就通過命令直接刪除,這種方式不僅可靠,而且對應用服務器沒有任何侵入性,可以很方便的實現。
腳本:
#!/bin/bash
##################################################
# vim /etc/crontab
# */30 * * * * root sh /mnt/rabbitmqMonitor/rabbitmq_monitor.cron
##################################################
#rabbitmq的環境變量
export RABBITMQPATH=/usr/lib/rabbitmq/bin
# 定義需要請求的隊列名稱數組
array_queue_name[0]="amz_RealTimeOrder:input"
array_queue_name[1]="amz_advertisement:request"
array_queue_name[2]="amz_advertisement:report"
array_queue_name[3]="mws:report:request_input"
array_queue_name[4]="mws:report:download_input"
array_queue_name[5]="amz_advertisement:info"
# 定義需要隊列所對應的最大值
declare -A queueMsgMaxMap
queueMsgMaxMap["amz_RealTimeOrder:input"]=1
queueMsgMaxMap["amz_advertisement:request"]=10000000000
queueMsgMaxMap["amz_advertisement:report"]=10000000000
queueMsgMaxMap["mws:report:request_input"]=100000000000
queueMsgMaxMap["mws:report:download_input"]=100000000000
queueMsgMaxMap["amz_advertisement:info"]=1000000000000
#獲取所有隊列的名字和每個隊列中的消息數量,存入'queueNum'數組中
queueIndex=0
for QUEUE in $(rabbitmqctl list_queues |grep -v 'Listing queues ...' | awk -F' ' '{print $1}');
do
for queue_name in ${array_queue_name[*]}
do
if [[ $QUEUE = $queue_name ]]; then
#統計每個消息隊列的數量
nums=$(rabbitmqctl list_queues |grep -w $QUEUE | awk -F' ' '{print $2}')
echo -e "startPush------------$nums-----------$QUEUE---------set>>>>${queueMsgMaxMap[$QUEUE]}--------"
# -ge
if [[ $nums -ge ${queueMsgMaxMap[$QUEUE]} ]]; then
#存key
queueName[$queueIndex]=$QUEUE
queueIndex=`expr $queueIndex + 1`
echo -e "maxPush------------$num-----------$QUEUE----------------"
fi
fi
done
done
#如果有異常,發送郵件
exceptionNum=${#queueName[@]}
if [[ $exceptionNum -gt 0 ]]; then
#有隊列阻塞,exceptionName存放的爲堵塞隊列的名稱,清空隊列
for name in ${queueName[*]}
do
$(rabbitmqctl -p / purge_queue $name)
echo -e "purge queue name>>>>>>>>>>>>>>$name"
done
echo "###################count at $(date +'%d-%m-%Y %H:%M:%S') ######################"
fi
注意事項:
消息堆積的時候除了要及時清理堆積消息,還要進行必要報警,像我們系統就是通過企業微信報警羣來報警的,一旦消息堆積,開發人員就可以馬上收到相關報警信息,並及時的進行處理。
還要非常重要的一點是,消息必須是無狀態的纔可以清空,不然一旦刪除將會導致數據丟失。我們在設計mq的時候,也要秉持着這種原則,因爲消息並不一定100%可靠,要做好消息丟失的措施。
想要更多幹貨、技術猛料的孩子,快點拿起手機掃碼關注我,我在這裏等你哦~
林老師帶你學編程:https://wolzq.com