設計場景
1、開啟Redis的鍵空間過期事件(鍵過期發布任務),創建訂單創建一個過期的key,按照訂單號為key,設置過期時間。
2、通過Redis的訂閱模式(持久阻塞),獲取到訂單號進行組裝。
3、Redis通過訂閱模式獲取到已經過期的key,把該key加入think-queue 任務隊列(依賴於Redis的隊列,隊列配置推薦使用Redis存儲消息)。
4、開啟一個隊列的消費守護進程,進行消費。如果消費失敗,則任務隊里不會自動刪除
遇到的問題
cli 模式報錯
[2019-11-20T14:33:25+08:00][ error ] [8]PDO::prepare(): send of 68 bytes failed with errno=32 Broken pipe
error
相關代碼
class RedisSubscribe
{
public function subscribe()
{
$redis = BaseRedis::plocal();
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
$redis->psubscribe(array('__keyevent@1__:expired'), function ($redis, $pattern, $chan, $msg) {
Log::info('[訂閱事件] 過期KEY ' . $msg);
$flag = strstr($msg,":");
if(empty($flag)){
Log::error('[訂閱事件] 非法的消息類型 '.$msg);
return false;
}
$originData = explode(':',$msg);
$event_key = $originData[1]??'0';
$event_status = $originData[0]??'0';
if($event_status == RedisTaskQueue::ORDER_TIMEOUT_EVENT){
Log::info('[訂閱事件] 訂單超時事件 '.$event_key);
$order = PayOrderModel::get(['order_no' => $event_key]);
Log::info('[訂閱事件] 訂單 '.$event_key);
if ($order['status'] == 0) {
$updateData = [
'id' => $order['id'],
'status' => 2,
'notify_status' => 0,
'pay_time' => time(),
'remark' => '超時隊列事件'
];
$res = PayOrderModel::update($updateData);
Log::info('[訂閱事件] 更新訂單 ' . json_encode($res));
}
}
});
}
}
問題就出現在以上代碼中查詢和更新數據庫問題
$order = PayOrderModel::get(['order_no' => $event_key]);
問題分析,這里采用的Redis的訂閱模式,持久性的
解決辦法,通過任務隊列解決
$isPushed = redis_queue(RedisTaskQueue::QUEUE_EVENT, $data);
把redis訂閱服務獲取到的信息,發布think-queue隊列中,在隊列中在進行處理數據庫相關操作
第二天出錯
[2019-11-21T09:12:41+08:00][ error ] [隊列事件] 隊列執行失敗 SQLSTATE[HY000]: General error: 2006 MySQL server has gone away|#0 /home/www/web/wiot.tinywan.com/thinkphp/library/think/db/Connection.php(844): think\db\Connection->query('SELECT * FROM `...', Array, false, false)
#1 /home/www/web/www.tinywan.com/thinkphp/library/think/db/Query.php(3132): think\db\Connection->find(Object(think\db\Query))
#2 /home/www/web/www.tinywan.com/thinkphp/library/think/db/Query.php(3193): think\db\Query->find(NULL)
數據庫配置開啟需要斷線重連(第一次出錯的時候正式環境沒有開啟該配置參數)
// 是否需要斷線重連
'break_reconnect' => false,
參考: