基于Swoft2.x框架实现php操作rabbitMQ

7 篇文章 0 订阅

RabbitMQ由于默认是基于AMQP协议 , php需要安装一下AMQP才能对RabbitMQ进行操作

由于swoft是基于swoole实现的框架 , swoole无法在windows使用 , 所以需要安装一套linux的环境 , 不会安装的找我的博客(Linux配置RabbitMQ)或自行百度

PHP安装AMQP客户端

composer require php-amqplib/php-amqplib

处理数据必读

使用了swoft自定义进程 , 并在bean.php中加载 , 不懂可自行百度或看我之前的文章(Swoft2.X 使用进程处理Redis队列_苗先生的PHP记录的博客-CSDN博客)

topic和fanout 都可不通过queue进行发送 , 也就是声明一个交换机,直接发送数据,详见我的topic的demo

这样子处理数据的时候只需要创建一个空名的queue,  通过routingkey读取数据

  • direct:路由模式 , 如果 routing key 匹配,那么 Message 就会被传递到相应的 queue , 如果没有处理的数据是会阻塞在这里等待消费
  • fanout:订阅模式 ,会向响应的 queue 广播 , 订阅模式是没有路由键routing_key的
  • topic:主题模式 , 通过路由键# 获取所有, *获取部分, 对 key 进行模式匹配,比如 ab* 可以传递到所有 ab* 的 queue
  • dlx: 死信队列 (延时队列) ,消息变成死信有以下几种情况

消息被拒绝(basic.reject / basic.nack),并且requeue = false

消息TTL过期

队列达到最大长度

direct路由模式(简单队列)

发送数据

控制器:

<?php

namespace App\Http\Controller;


use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use Swoft\Http\Server\Annotation\Mapping\Controller;
use Swoft\Http\Server\Annotation\Mapping\RequestMapping;

/**
 * @Controller(prefix="rabbit")
 */
class Rabbit
{
    /**
     * @RequestMapping(route="index")
     */
    public function index()
    {
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->queue_declare('swoft_queue_test', false,false,false,false);
        # 消息
        $msg = new AMQPMessage('swoft_queue_test'.time(),[
            'content_type' => 'text/plain',
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );
        # 发送
        $channel->basic_publish($msg,'','swoft_queue_test');
        $channel->wait_for_pending_acks();

        $channel->close();
        $connect->close();
    }
    
   
}

处理数据

<?php

namespace App\Process;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\UserProcess;
use Swoole\Timer;

/**
 * @Bean()
 */
class RabbitMQProcess extends UserProcess
{

    /**
     * @param \Swoft\Process\Process $process
     * @return void
     */
    public function run(\Swoft\Process\Process $process):void
    {
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->queue_declare('swoft_queue_test', false,false,false,false,false);
        # 回调
        $callback = function ($msg){
            
            echo $msg->body.PHP_EOL;
        };
        $channel->basic_consume('swoft_queue_test','',false,true,false,false,$callback);
        while(count($channel->callbacks)){
            $channel->wait();
        }
        $channel->close();
        $connect->close();
    }
}

订阅模式fanout

发送数据

 /**
     * 订阅模式
     * @RequestMapping(route="fanout")
     */
    public function fanout()
    {
        $exchangeName = 'fanout-ex';
        $queueName='fanout-test';
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'fanout',false,true,false);
        $channel->queue_declare($queueName, false,false,false,false);
        $channel->queue_bind($queueName,$exchangeName);
        # 消息
        $msg = new AMQPMessage('订阅消息: '.time(),[
            'content_type' => 'text/plain',
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );
        # 发送
        $channel->basic_publish($msg,$exchangeName);
        $channel->wait_for_pending_acks();

        $channel->close();
        $connect->close();
    }

处理数据

<?php

namespace App\Process;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\UserProcess;

/**
 * @Bean()
 * 订阅模式
 */
class RabbitMQFanoutProcess extends UserProcess
{

    /**
     * @param \Swoft\Process\Process $process
     * @return void
     */
    public function run(\Swoft\Process\Process $process):void
    {
        $exchangeName = 'fanout-ex';
        $queueName='fanout-test';
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'fanout',false,true,false);
        $channel->queue_declare($queueName, false,false,false,false);
        $channel->queue_bind($queueName,$exchangeName);
        # 回调
        $callback = function ($msg){
            echo '订阅'.$msg->body.PHP_EOL;
        };
        $channel->basic_consume($queueName,'',false,true,false,false,$callback);
        while(count($channel->callbacks)){
            $channel->wait();
        }
        $channel->close();
        $connect->close();
    }
}

主题模式topic

发送数据

我这里声明了三个路由键 , 分别是

A方法: a.route.queue

B方法: b.route.queue

C方法: c.routes.queue

发送主题模式数据是不需要 $channel->queue_declare声明队列的

    /**
     * 主题模式
     * @RequestMapping(route="topic-a",method={"GET"})
     */
    public function topica()
    {
        $exchangeName = 'topic-ex';
        $routingKey = 'a.route.queue';
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'topic',false,true,false);
        # 消息
        $msg = new AMQPMessage('主题消息:a.route.queue '.time(),[
            'content_type' => 'text/plain',
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );
        # 发送
        $channel->basic_publish($msg,$exchangeName,$routingKey);
        $channel->wait_for_pending_acks();

        $channel->close();
        $connect->close();
    }

    /**
     * 主题模式
     * @RequestMapping(route="topic-b",method={"GET"})
     */
    public function topicb()
    {
        $exchangeName = 'topic-ex';
        $routingKey = 'b.route.queue';
        $connect = new AMQPStreamConnection('39.105.156.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'topic',false,true,false);
        # 消息
        $msg = new AMQPMessage('主题消息:b.route.queue '.time(),[
            'content_type' => 'text/plain',
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );
        # 发送
        $channel->basic_publish($msg,$exchangeName,$routingKey);
        $channel->wait_for_pending_acks();

        $channel->close();
        $connect->close();
    }

    /**
     * 主题模式
     * @RequestMapping(route="topic-c",method={"GET"})
     */
    public function topicc()
    {
        $exchangeName = 'topic-ex';
        $routingKey = 'c.routes.queue';
        $connect = new AMQPStreamConnection('39.105.156.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'topic',false,true,false);
        # 消息
        $msg = new AMQPMessage('主题消息:c.routes.queue '.time(),[
            'content_type' => 'text/plain',
            'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
        ]);

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );
        # 发送
        $channel->basic_publish($msg,$exchangeName,$routingKey);
        $channel->wait_for_pending_acks();

        $channel->close();
        $connect->close();
    }

处理数据

这里可以使用通配符 * #来获取不同路由键的数据

比如我这里*.routes.* , 只能获取到c.routes.queue

而# 能获取到所有的数据

这里通过声明一个空名队列绑定不同的路由键 , 达到获取不同路由键的数据

通配符*

<?php

namespace App\Process;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\UserProcess;

/**
 * @Bean()
 * topic主题模式
 */
class RabbitMQTopicPProcess extends UserProcess
{

    /**
     * @param \Swoft\Process\Process $process
     * @return void
     */
    public function run(\Swoft\Process\Process $process):void
    {
        $exchangeName = 'topic-ex';
        $queueName='topic-test';
        $routingKey = "*.routes.queue";
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'topic',false,true,false);
        list($queueName,,) =  $channel->queue_declare('', false,true,true,false);
        $channel->queue_bind($queueName,$exchangeName,$routingKey);
        # 回调
        $callback = function ($msg){
            echo '接收消息topic * :'.$msg->body.PHP_EOL;
        };
        $channel->basic_consume($queueName,'',false,true,false,false,$callback);
        while(count($channel->callbacks)){
            $channel->wait();
        }
        $channel->close();
        $connect->close();
    }
}

通配符#

<?php

namespace App\Process;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\UserProcess;

/**
 * @Bean()
 * topic主题模式
 */
class RabbitMQTopicAProcess extends UserProcess
{

    /**
     * @param \Swoft\Process\Process $process
     * @return void
     */
    public function run(\Swoft\Process\Process $process):void
    {
        $exchangeName = 'topic-ex';
        $queueName='topic-test';
        $routingKey = "#";
        $connect = new AMQPStreamConnection('39.105.106.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare($exchangeName,'topic',false,true,false);
        list($queueName,,) =  $channel->queue_declare('', false,true,true,false);
        $channel->queue_bind($queueName,$exchangeName,$routingKey);
        # 回调
        $callback = function ($msg){
            echo '接收消息topic # :'.$msg->body.PHP_EOL;
        };
        $channel->basic_consume($queueName,'',false,true,false,false,$callback);
        while(count($channel->callbacks)){
            $channel->wait();
        }
        $channel->close();
        $connect->close();
    }
}

死信队列

必须两个交换机是fanout类型

我找视频资料 , A交换机可以是headers, 这里我没去测试, 回来补充一下

发送数据

 /**
     * 延时队列
     * 使用场景: 订单超时 , 自动收货 , 会员到期 续费提醒等
     * 需要两个队列 , Normal队列设置过期时间等参数 , 到期后投送到DLX队列进行消费
     * 死信队列: A生产者->A交换机->A队列,设置ttl过期时间 -> A队列绑定B交换机 ->B队列 ->消费者
    A队列 -> B队列 条件: ttl到期 , 消息拒绝 ,队列达到最大长度 等
     * 交换机必须是fanout , 其他我测试没有反应, 文档没找到原因(可能我英文太菜了,翻译看的有问题)
     * @RequestMapping(route="dlx")
     */
    public function delay()
    {
        $exchangeDlx = 'exchange.test.dlx';
        $exchangeNormal = 'exchange.test.normal';
        $queueDlx='queue.test.dlx';
        $queueNormal='queue.test.normal';
        $routingKey='routingkey';// 这里我测试绑定routingkey
        $routingKey='';
        $connection = new AMQPStreamConnection('39.105.156.191',5672,'guest','guest');
        $channel = $connection->channel();

        $channel->exchange_declare($exchangeDlx, 'fanout', false, true,false);
        $channel->exchange_declare($exchangeNormal, 'fanout', false, true,false);
        $args = new AMQPTable();
        // 消息过期方式:设置队列中的消息10s之后过期
        $args->set('x-message-ttl', 3000);
        $args->set('x-dead-letter-exchange', $exchangeDlx); //消息到期发给这个交换机
//        $args->set('x-dead-letter-routing-key', $routingKey); // 这里我测试绑定routingkey
        $channel->queue_declare($queueNormal, false, true, false, false, false, $args);
        $channel->queue_declare($queueDlx, false, true, false, false);
        $channel->queue_bind($queueNormal, $exchangeNormal);
        $channel->queue_bind($queueDlx, $exchangeDlx, $routingKey); // 交换机接收到消息后发送到这个队列
        $message = new AMQPMessage('死信队列消息'.time());

        $channel->confirm_select(); // 发布确认模式

        //推送成功
        $channel->set_ack_handler(
            function (AMQPMessage $message) {
                echo "发送成功: " . $message->body . PHP_EOL;
            }
        );

        //推送失败
        $channel->set_nack_handler(
            function (AMQPMessage $message) {
                echo "发送失败: " . $message->body . PHP_EOL;
            }
        );

        # 发送
        $channel->basic_publish($message,$exchangeNormal,'rks');
        $channel->wait_for_pending_acks();

        $channel->close();
        $connection->close();
    }

处理数据

<?php

namespace App\Process;

use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Process\UserProcess;
use Swoole\Timer;

/**
 * @Bean()
 * DLX死信队列(延时队列)
 */
class RabbitMQDlxProcess extends UserProcess
{

    /**
     * @param \Swoft\Process\Process $process
     * @return void
     */
    public function run(\Swoft\Process\Process $process):void
    {
        $connect = new AMQPStreamConnection('39.105.156.191',5672,'guest','guest');
        $channel = $connect->channel();
        $channel->exchange_declare('exchange.test.dlx', 'fanout', false, true,false);
        $channel->queue_declare('queue.test.dlx', false,true,false,false,false);
        $channel->queue_bind('queue.test.dlx', 'exchange.test.dlx');
//        $channel->queue_bind('queue.test.dlx', 'exchange.test.dlx', 'routingkey'); // 这里我测试绑定routingkey
        # 回调
        $callback = function ($msg){
            echo '接收时间:'.time().'发送时间:'.$msg->body.PHP_EOL;
            // 手动应答
            $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
        };
        // 公平调度
        $channel->basic_qos(null, 1, null);
        // 第四参数为自动应答, 设置为false
        $channel->basic_consume('queue.test.dlx','',false,false,false,false,$callback);
        while(count($channel->callbacks)){
            $channel->wait();
        }
        $channel->close();
        $connect->close();
    }

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苗先生的PHP记录

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值