在業務中有時會碰到延遲操作,如下單後半小時未支付則取消訂單、下單後十五分鐘未支付則傳簡訊提醒等等。那這樣的需求如何去實現呢。
我們以訂單建立15分鐘後未支付,給使用者傳送郵件為場景進行學習
//建立訂單的邏輯/** * 隨機建立訂單 */$order = [ 'order_number' => mt_rand(100,10000).date("YmdHis"), 'user_id' => mt_rand(1, 100), 'order_amount' => mt_rand(100, 1000),]; /**@var $manager IlluminateDatabaseCapsuleManager **/ $conn = $manager;$insertResult = $conn::table("order") ->insert($order);print_r($insertResult);
延遲處理邏輯
while(true) { // 未支付訂單列表 $orderList = $conn::table("order") ->where("created_time", '<=', date("Y-m-d H:i:s", strtotime("-15 minutes"))) ->where('sended_need_pay_notify', '=', 2) ->where('status', '=', 1) ->select(['user_id', 'id']) ->orderBy("id", 'asc') ->get(); $orderList = json_decode(json_encode($orderList), true); foreach ($orderList as $orderInfo) { sendEmail($orderInfo['user_id']); $conn::table('order') ->where('id', '=', $orderInfo['id']) ->update(['sended_need_pay_notify' => 1]); logs("update-success-orderId-". $orderInfo['id']."-userId-".$orderInfo['user_id']); } sleep(10);}
執行處理指令碼
[email protected] delay_mq_demo % php first_while_handler.php send email to 73 success ... 2020-06-24 11:37:36:update-success-orderId-3-userId-73
這種方式吧實現簡單,但是不優雅,同時大批次訂單產生也會遇到問題。
// 建立訂單的邏輯try { /** * 隨機建立訂單 */ $order = [ 'order_number' => mt_rand(100,10000).date("YmdHis"), 'user_id' => mt_rand(1, 100), 'order_amount' => mt_rand(100, 1000), ]; /**@var $manager IlluminateDatabaseCapsuleManager **/ $conn = $manager; $insertId = $conn::table("order") ->insertGetId($order); $body = json_encode(['order_id' => $insertId, 'created_time' => date("Y-m-d H:i:s")]); $publishMessage = new TopicMessage( $body ); // 設定訊息KEY $publishMessage->setMessageKey("MessageKey"); // 定時訊息, 定時時間為3分鐘後 $publishMessage->setStartDeliverTime(time() * 1000 + 3 * 60 * 1000); $result = $this->producer->publishMessage($publishMessage); print "Send mq message success. msgId is:" . $result->getMessageId() . ", bodyMD5 is:" . $result - >getMessageBodyMD5() . "n"; } catch (Exception $e) { print_r($e->getMessage() . "n"); }
消費邏輯 同樣是在消費者中處理
foreach ($messages as $message) { $receiptHandles[] = $message->getReceiptHandle(); $messageBody = $message->getMessageBody(); $orderInfo = json_decode($messageBody, true); if (!empty($orderInfo['order_id'])) { $orderId = $orderInfo['order_id']; /**@var $manager IlluminateDatabaseCapsuleManager * */ $conn = $manager; $orderInfo = $conn::table("order") ->select(['id', 'user_id']) ->where('id', '=', $orderId) ->where('status', '=', 1) ->first(); if (!empty($orderInfo)) { $orderInfo = json_decode(json_encode($orderInfo), true); sendEmail($orderInfo['user_id']); $conn::table('order') ->where('id', '=', $orderInfo['id']) ->update(['sended_need_pay_notify' => 1]); logs("update-success-orderId-" . $orderInfo['id'] . "-userId-" . $orderInfo['user_id']); } } }
啟動生產一條訊息
[email protected] delay_mq_demo % php rocket_mq_handler_producer.php Send mq message success. msgId is:76CF2135696C3D4EAC698A9FA1E1879D, bodyMD5 is:63448B50AA7B8AF47B07AA7CE807E3D3 [email protected] delay_mq_demo %
啟動消費者慢慢等待
[email protected] delay_mq_demo % php rocket_mq_handler_consumer.php No message, contine long polling!RequestId:5EF752583441411C74869BA9 No message, contine long polling!RequestId:5EF7525B3441411C74869FE2 No message, contine long polling!RequestId:5EF7525E3441411C7486A42C No message, contine long polling!RequestId:5EF752613441411C7486A7D9 consume finish, messages:send email to 95 success ...2020-06-27 12:08:05:update-success-orderId-8-userId-95 Array( [0] => 76CF2135696C3D4EAC698A9FA1E1879D-MCAxNTkzMjY2NzkxNDM5IDMwMDAwMCAzIDAgYmpzaGFyZTUtMDggNSAw) ack
這種方式有現有的服務可以使用,減少開發時間
// 生產者$exchange = 'order15min_notify_exchange'; $queue = 'order15minx_notify_queue';$dlxExchange = "dlx_order15min_exchange"; $dlxQueue = "dlx_order15min_queue"; $connection = new AMQPStreamConnection(getenv('RABBIT_HOST'), getenv('RABBIT_PORT'), getenv("RABBIT_USER"), getenv("RABBIT_PASS"), getenv("RABBIT_VHOST")); $channel = $connection->channel();$channel->exchange_declare($exchange, AMQPExchangeType::DIRECT, false, true, false); $channel->exchange_declare($dlxExchange, AMQPExchangeType::DIRECT, false, true, false);// 設定佇列的過期時間// 正常佇列$table = new PhpAmqpLibWireAMQPTable();// 訊息有效期$table->set('x-message-ttl', 3*60*1000);$table->set("x-dead-letter-exchange", $dlxExchange);$channel->queue_declare($queue, false, true, false, false, false, $table);$channel->queue_bind($queue, $exchange);// 死信佇列$channel->queue_declare($dlxQueue, false, true, false, false, false);$channel->queue_bind($dlxQueue, $dlxExchange);/** * 隨機建立訂單 */$order = [ 'order_number' => mt_rand(100,10000).date("YmdHis"), 'user_id' => mt_rand(1, 100), 'order_amount' => mt_rand(100, 1000),];/**@var $manager IlluminateDatabaseCapsuleManager **/$conn = $manager;$insertId = $conn::table("order") ->insertGetId($order);$messageBody = json_encode(['order_id' => $insertId, 'created_time' => date("Y-m-d H:i:s")]); $message = new AMQPMessage($messageBody, array('content_type' => 'text/plain', 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)); $channel->basic_publish($message, $exchange);
消費者
$dlxExchange = "dlx_order15min_exchange";$dlxQueue = "dlx_order15min_queue"; $connection = new AMQPStreamConnection(getenv('RABBIT_HOST'), getenv('RABBIT_PORT'), getenv("RABBIT_USER"), getenv("RABBIT_PASS"), getenv("RABBIT_VHOST")); $channel = $connection->channel(); $channel->queue_declare($dlxQueue, false, true, false, false);$channel->exchange_declare($dlxExchange, AMQPExchangeType::DIRECT, false, true, false); $channel->queue_bind($dlxQueue, $dlxExchange);/** * @param PhpAmqpLibMessageAMQPMessage $message */function process_message($message){ echo "n--------n"; echo $message->body; echo "n--------n"; $orderInfo = json_decode($message->body, true); if (!empty($orderInfo['order_id'])) { $orderId = $orderInfo['order_id']; /**@var $conn IlluminateDatabaseCapsuleManager * */ $conn = getdb(); $orderInfo = $conn::table("order") ->select(['id', 'user_id']) ->where('id', '=', $orderId) ->where('status', '=', 1) ->first(); if (!empty($orderInfo)) { $orderInfo = json_decode(json_encode($orderInfo), true); sendEmail($orderInfo['user_id']); $conn::table('order') ->where('id', '=', $orderInfo['id']) ->update(['sended_need_pay_notify' => 1]); logs("update-success-orderId-" . $orderInfo['id'] . "-userId-" . $orderInfo['user_id']); } } $message->delivery_info['channel']->basic_ack( $message->delivery_info['delivery_tag']);}$channel->basic_consume($dlxQueue, $consumerTag, false, false, false, false, 'process_message');
啟動消費者
[email protected] delay_mq_demo % php rabbit_mq_handler_consumer.php -------- {"order_id":7,"created_time":"2020-06-27 11:50:08"} -------- send email to 2 success ... 2020-06-27 11:56:55:update-success-orderId-7-userId-2
分別啟動消費者、生產者就可以了,這裡面訊息的流轉可以看到
訊息先進入到正常佇列,過期後進入了死信佇列而被消費
程式碼範例:github.com/nobody05/delay_mq_demo
以上就是PHP 簡單實現延時操作的詳細內容,更多請關注TW511.COM其它相關文章!