第一次寫部落格,記錄一下RabbitMQ的學習,如果有不正確的地方,還請及時指出
場景
訂單超時未支付,關閉訂單
使用者下單
public function index()
{
//建立訂單
$order = new Order();
$order->order_sn = date('YmdHis').time();
$order->user_id = 1;
$order->product_id = 1;
$order->save();
//推送至佇列
(new OrderService())->push($order);
//返回相關資訊
return true;
}
佇列
<?php
namespace App\Service;
use App\Models\Order;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
class OrderService
{
const HOST = '192.168.1.199';
const PORT = '5672';
const LOGIN = 'guest';
const PASSWORD = 'guest';
const VHOST = '/';
//交換機名稱
public $exchangeName = 'laravel_exchange_name';
//普通佇列名稱和路由key
public $queueName = 'laravel_queue_name';
public $routeKey = 'laravel_route_key';
//延遲佇列和路由
public $delayQueueName = 'laravel_delay_queue_name';
public $delayRouteKey = 'laravel_delay_route_key';
//延遲時長
public $delaySecond = 10;
public $channel;
public function __construct()
{
$connection = new AMQPStreamConnection(self::HOST,self::PORT,self::LOGIN,self::PASSWORD);
$this->channel = $connection->channel();
$this->init();
}
public function init()
{
// 宣告交換機
$this->channel->exchange_declare($this->exchangeName, 'direct', false, true, false);
$this->declareConsumeQueue();
$this->declareDelayQueue();
}
//消費佇列
private function declareConsumeQueue()
{
//宣告消費佇列
$this->channel->queue_declare($this->queueName, false, true, false, false);
//繫結交換機及佇列
$this->channel->queue_bind($this->queueName, $this->exchangeName, $this->routeKey);
}
//延遲佇列
private function declareDelayQueue()
{
//設定訊息過期時間
$tab = new AMQPTable([
'x-dead-letter-exchange' => $this->exchangeName, //訊息過期後推送至此交換機
'x-dead-letter-routing-key' => $this->routeKey, //訊息過期後推送至此路由地址 //也就是我們消費的正常佇列 與①對應
'x-message-ttl' => intval($this->delaySecond) * 1000, //10秒
]);
//宣告延遲佇列
$this->channel->queue_declare($this->delayQueueName,false,true,false,false,false,$tab);
//繫結交換機及延遲佇列
$this->channel->queue_bind($this->delayQueueName, $this->exchangeName, $this->delayRouteKey);
}
//入佇列
public function push($order)
{
$message = json_encode([
'id' => $order->id
]);
//建立訊息
$msg = new AMQPMessage($message, [
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
]);
//推送至佇列 //訊息 //交換機名稱 //路由 推送至延遲佇列中
$this->channel->basic_publish($msg, $this->exchangeName, $this->delayRouteKey);
}
//出佇列
public function consume()
{
//消費 普通消費佇列 //①
$this->channel->basic_consume($this->queueName, '', false, false, false, false,
[$this, 'process_message']);
while (count($this->channel->callbacks)) {
$this->channel->wait();
}
}
//開始消費
public function process_message($message)
{
$obj = json_decode($message->body);
try {
$order = Order::find($obj->id);
if (strtotime($order->created_at) + $this->delaySecond > time()){
throw new \Exception('取消訂單時間未到', 404);
}
//更改資料庫狀態
$order->status = 10;
$order->colsed_at = date('Y-m-d H:i:s');
$res = $order->save();
if (!$res){
throw new \Exception('取消訂單失敗', 404);
}
} catch (\Exception $e) {
//記錄日誌
}
//確認訊息處理完成
$message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
}
}
使用者下單
當使用者下單以後,延遲佇列就會出現一條待消費的記錄,這裡佇列名稱和我們程式碼中生成的名稱一致,
laravel_delay_queue_name
當訊息過期以後,此訊息就會被推送至我們設定好的佇列中,也就是
laravel_queue_name
,從而會被消費掉,達到超時未支付,取消訂單的效果
定義排程
<?php
namespace App\Console;
use App\Console\Commands\OrderNopay;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
//省略其他程式碼
protected $commands = [
OrderNopay::class,
];
}
<?php
namespace App\Console\Commands;
use App\Service\OrderService;
use Illuminate\Console\Command;
class OrderNopay extends Command
{
protected $signature = 'order:nopay';
protected $description = '訂單超時未支付';
public function __construct()
{
parent::__construct();
}
public function handle(OrderService $order)
{
$order->consume();
}
}
執行
可以使用
php artisan order:nopay
命令,或者通過supervisor
來跑
本作品採用《CC 協議》,轉載必須註明作者和本文連結