相信很多小夥伴都在開發中使用過消息隊列,尤其是高併發的情況,一般可以在緩存中操作數據,然後通過消息異步處理業務邏輯,操作數據庫等。
本人所在的公司使用了阿里雲的消息隊列和RabbitMQ,據說使用阿里雲消息隊列的一部分原因是RabbitMQ實現延遲消息比較複雜,要依賴死信...接下來進入主題,說說我是怎麼使用消息和遇到的坑吧~
一般來講,我們可以使用一個腳本來接收阿里雲消息處理業務邏輯,但是如果業務量特別大的話,我們可能會遇到一個問題,就是腳本處理不過來,消息積攢的數量可能遠遠超出每秒能夠處理的數量。針對這種現象,我們可以啓動多個處理腳本來同時處理消息,這樣會明顯加快處理消息的速度。
但是,多個腳本同時從一條消息隊列裏面取消息的時候,會不會同時取到同一條消息,然後造成消息重複處理的現象呢?我覺得肯定是會的,消息是第三方服務,我們無法保證他的100%穩定,所以我們需要在處理的時候下點功夫了。
我們發送消息的數據體是json,一般我們會在每條消息裏面加一個taskid,以時間戳(精確到毫秒級) + 隨機數組成,這個taskid足夠長,我們得以保證他不會重複(重複的可能性極小,類似於mongodb的主鍵也是這個原理)。
接下來看一段代碼:
<?php
try {
//僞代碼
$getData = $mq->receive();
$getData = \Zend\Json\Json::decode($getData, true); //如果不是json數據 我們可以捕獲到異常
//先檢測數據
$errorMsg = [];
if (!isset($getData['taskid']) || $getData['taskid'] == '') {
$errorMsg[] = 'taskid不能爲空';
}
if (!isset($getData['order_id']) || $getData['order_id'] == '') {
$errorMsg[] = '訂單號不能爲空';
}
if (!cache()->setex($this->cachePrefix . $getData['taskid'], 1)) {
$errorMsg[] = '該任務已被處理';
}
if (count($errorMsg) >= 1) {
//記日誌 方便排錯
$this->log('xxxx處理腳本錯誤 哪個文件 錯誤級別 錯誤原因' . implode('|', $errorMsg));
return false;
}
//必須有過期時間 不然會把redis撐爆
cache()->expire($this->cachePrefix . $getData['taskid'], 7 * 86400);
/**
* 處理業務邏輯
*/
//業務邏輯處理正常,刪除redis鎖,刪除消息
cache()->del($this->cachePrefix . $getData['taskid']);
$mq->ack();
return true;
}
//捕獲到了異常
catch (\Exception $e) {
//一定要把這次消息刪掉 不然會重複進來 日誌錯誤級別記高一點 手動處理問題
$mq->ack();
//記日誌
$this->log();
}
小夥伴們知道這段代碼哪裏有問題嗎?