Rabbitmq - TP6应用

1.配置文件

<?php

// +----------------------------------------------------------------------
// | rabbitMQ 示例配置
// 
//   Exchange在定义的时候是有类型的,以决定到底是哪些Queue符合条件,可以接收消息:
//                      fanout:所有bind到此exchange的queue都可以接收消息
//                      direct:通过routingKey和exchange决定的那个唯一的queue可以接收消息
//                      topic:所有符合routingKey(此时可以是一个表达式)的routingKey所bind的queue可以接收消息
//                      headers:通过headers 来决定把消息发给哪些queue(这个很少用)
//                      x-delayed-message:插件安装,延迟模式
// +----------------------------------------------------------------------


return [

    'AMQP' => [
        'host'     => '127.0.0.1',
        'vhost'    => '/',
        'port'     => '5672',
        'user'     => 'admin',
        'password' => 'admin'
    ],
    'dead_queue_info' => [
        'dead_queue_name'  => 'dead_queue',
        'dead_exc_name'    => 'dead-exc',
        'dead_routing_key' => 'dead_key'
    ],
    //订单支付队列
    'order_pay' => [
        'exchange_name'  => 'order_change',     //交换器名称
        'exchange_type'  => 'x-delayed-message',           //交换机运行模式    
        'queue_name'     => 'order_queue',      //队列名称   
        'route_key'      => 'order',            //路由键,用于绑定队列与交换机
        'consumer_tag'   => 'order',             //消费标签     
        'ttl'            => 10000 //10s 毫秒为单位
    ]
];

2.新建RabbitMq.php

<?php

namespace app\controller;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;

class RabbitMq
{
    static private $instance;
    static private $connection;
    static private $channel;
    static private $exchangeName = '';
    static private $scene = '';
    static private $config = [];
    static private $dead_config = [];

    /**
     * RabbitMq constructor.
     * @param $exchangeType
     */
    private function __construct($scene)
    {
        // 取默认配置
        $config = config('rabbitmq.AMQP');

        //创建链接
        self::$connection = new AMQPStreamConnection($config['host'], $config['port'], $config['user'], $config['password'], $config['vhost']);
        self::$channel = self::$connection->channel();
        if (!empty($scene)) {
            self::$config = $info = config('rabbitmq.' . $scene);
            self::$scene = $scene;
            self::$exchangeName = $info['exchange_name'];
            self::$dead_config =  config('rabbitmq.dead_queue_info');
            self::$channel->exchange_declare(
                self::$exchangeName, //交换机名称
                $info['exchange_type'], //路由类型
                false, //don't check if a queue with the same name exists 是否检测同名队列
                true, //the queue will not survive server restarts 是否开启队列持久化
                false //the queue will be deleted once the channel is closed. 通道关闭后是否删除队列
            );
        }
    }

    /**
     * 实例化
     * @param string $exchangeType
     * @return RabbitMq
     */
    public static function instance($scene = '')
    {
        if (!self::$instance instanceof self) {
            self::$instance = new self($scene);
        }
        return self::$instance;
    }

    /**
     * 防止被外部复制
     */
    private function __clone()
    {
    }


    /**
     * 发送(直接交换机)
     * @param $routingKey
     * @param string $data
     * @param string $queue_name 队列名
     * @param string $delay 是否延迟 1是 0否 (若延迟则开启死信队列)
     */
    public function sendDirect($data = "", $delay = 0)
    {
        $arr = ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT];

        if (empty($data)) $data = "Hello World!";
        if ($delay) {
            //延迟队列配合死信队列

            //声明死信交换器
            self::$channel->exchange_declare(self::$dead_config['dead_exc_name'], 'direct', false, false, false);
            //声明死信队列
            self::$channel->queue_declare(self::$dead_config['dead_queue_name'], false, true, false, false);
            //死信队列绑定死信交换器
            self::$channel->queue_bind(self::$dead_config['dead_queue_name'],  self::$dead_config['dead_exc_name'],  self::$dead_config['dead_routing_key']);

            //
            $args = new AMQPTable(['x-delayed-type' => 'direct']);
            //过期时间
            $arr['application_headers'] = new AMQPTable(['x-delay' => self::$config['ttl']]);
        }
        //设置队列名
        self::$channel->queue_declare(self::$config['queue_name'], false, true, false, false, false, $args);
        //队列绑定交换器
        self::$channel->queue_bind(self::$config['queue_name'],  self::$exchangeName, self::$config['route_key']);


        //设置消息持久化以及过期时间
        $msg = new AMQPMessage($data, $arr);
        self::$channel->basic_publish($msg, self::$exchangeName, self::$config['route_key']);
        echo "[x] Sent" . self::$config['route_key'] . ":$data \n";
    }

    /**
     * 接收(直接交换机)
     * @param \Closure $callback
     * @param array $bindingKeys
     */
    public function receiveDirect(\Closure $callback)
    {
        //随机生成队列名(生成后返回的是数组,只有第一个元素是队列名) 临时生成的,退出程序则消失
        // list($queue_name,,) = $channel->queue_declare('', false, false, true, false);

        // self::$channel->queue_declare(self::$config['queue_name'], false, true, true, false);
        // foreach ($bindingKeys as $bindingKey) {
        self::$channel->queue_bind(self::$config['queue_name'], self::$exchangeName, self::$config['route_key']);
        // }
        //一次只消费一条消息,未确认消费前不可消费其他消息
        self::$channel->basic_qos(null, 1, null);
        self::$channel->basic_consume(self::$config['queue_name'], "", false, true, false, false, $callback);

        while (count(self::$channel->callbacks)) {
            self::$channel->wait();
        }
    }
    /**
     * 销毁
     */
    public function __destruct()
    {
        // TODO: Implement __destruct() method.
        self::$channel->close();
        self::$connection->close();
    }
}

3.新建RabbitMqWork.php

<?php

namespace app\controller;

use app\controller\RabbitMq;

// RabbitMq 各个模式方法操作类
class RabbitMqWork
{
    private $RabbitMq;
    private $scene;
    private $config;
    //场景
    public function __construct($scene = '')
    {
        $this->scene = $scene;
        $this->config = config('rabbitmq.' . $scene);
        $this->RabbitMq = RabbitMq::instance($scene);
    }

    /**
     * 发送(直接交换机)
     * @param $bindingKey
     * @param $data
     */
    public function sendDirect($data,  $delay = 0)
    {
        $this->RabbitMq->sendDirect($data, $delay);
    }

    /**
     * 接收(直接交换机)
     * @param \Closure $callback
     * @param array $bindingKeys
     */
    public function receiveDirect(\Closure $callback, array $bindingKeys)
    {
        $this->RabbitMq->receiveDirect($callback, $bindingKeys);
    }
}

4.业务代码

 public function index()
    {
        $order_id = createFakeId();
        $order_info = [
            'product_type' => 'compute',
            'order_amount' => '10000',
            'pay_time'     => '2022-09-02 18:14:00',
            'config'       => '4C4G',
            'area'         => '美国'
        ];

        $redis = redis_connect();
        $redis->setEx($order_id, 600, json_encode($order_info,JSON_UNESCAPED_UNICODE));
        $rabbitmq = new RabbitMqWork('order_pay');
        $rabbitmq->sendDirect($order_id, 1);
    }

5.消费消息

注:tp中需使用自定义命令行(cli)进行消费
php think order & //加&可后台执行

<?php

declare(strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use app\controller\RabbitMqWork;

class order extends Command
{
    protected function configure()
    {
        // 指令配置
        $this->setName('app\command\order')
            ->setDescription('the app\command\order command');
    }

    protected function execute(Input $input, Output $output)
    {
        // 指令输出
        // $output->writeln('app\command\order');
        $redis = redis_connect();

        $info = config('rabbitmq.order_pay');
        $rabbitmq = new RabbitMqWork('order_pay');
        $callback =  function ($msg) use ($redis) {
            $rid = $msg->body;
            $info = json_decode($redis->get($rid), 1);
            if (!$info) debugLog('rabbitmq', '消息' . $rid . '已被消费');
            //todo 执行业务逻辑

            //执行结束,清除缓存
            $redis->del($rid);
        };
        $rabbitmq->receiveDirect($callback, [$info['route_key']]);
    }
}

posted @ 2022-09-07 14:37  Myifb  阅读(598)  评论(0)    收藏  举报