PHP利用rabbitmq处理队列


安装rabbitmq-c

wget https://github.com/alanxz/rabbitmq-c/archive/v0.9.0.tar.gz
tar xf v0.9.0.tar.gz
cd rabbitmq-c-0.9.0
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/rabbitmq-c ..
cd build/
cmake --build . --target install
cd /usr/local/rabbitmq-c/
ln -s lib64 lib

/usr/local/rabbitmq-c是等下安装PHP扩展amqp需要用的路径

pecl安装php扩展amqp

sudo pecl install amqp

中途会出现:Set the path to librabbitmq install prefix [autodetect] :
遇见提示需要需要librabbitmq路径是否输入:
上面安装的/usr/local/rabbitmq-c
安装完毕后 php -m |grep amqp 查看是否安装成功

二、php使用demo

1.消费者

第一次执行,应该先运行消费者,消费者才可以创建Exchange和queue


<?php
/**
 * Created by PhpStorm.
 * User: jmsite.cn
 * Date: 2019/1/15
 * Time: 13:16
 */
//声明连接参数
$config = array(
    'host' => '192.168.6.210',
    'vhost' => '/',
    'port' => 5672,
//    'login' => 'test',
//    'password' => 'test'
);
//连接broker
$cnn = new AMQPConnection($config);
if (!$cnn->connect()) {
    echo "Cannot connect to the broker";
    exit();
}
//在连接内创建一个通道
$ch = new AMQPChannel($cnn);
//创建一个交换机
$ex = new AMQPExchange($ch);
//声明路由键
$routingKey = 'key_1';
//声明交换机名称
$exchangeName = 'exchange_1';
//设置交换机名称
$ex->setName($exchangeName);
//设置交换机类型
//AMQP_EX_TYPE_DIRECT:直连交换机
//AMQP_EX_TYPE_FANOUT:扇形交换机
//AMQP_EX_TYPE_HEADERS:头交换机
//AMQP_EX_TYPE_TOPIC:主题交换机
$ex->setType(AMQP_EX_TYPE_DIRECT);
//设置交换机持久
$ex->setFlags(AMQP_DURABLE);
//声明交换机
$ex->declareExchange();
//创建一个消息队列
$q = new AMQPQueue($ch);
//设置队列名称
$q->setName('queue_1');
//设置队列持久
$q->setFlags(AMQP_DURABLE);
//声明消息队列
$q->declareQueue();
//交换机和队列通过$routingKey进行绑定
$q->bind($ex->getName(), $routingKey);
//接收消息并进行处理的回调方法
function receive($envelope, $queue) {

    //随机失败
    $t= rand(1, 2);//在1~2之间取一个数

    //休眠两秒,
 //   sleep(3);
    //echo消息内容
    echo $envelope->getBody()."\n";
    //显式确认,队列收到消费者显式确认后,会删除该消息

    if($t==1){
        var_dump('ack');
        $queue->ack($envelope->getDeliveryTag());
    }else{
        var_dump('nack');
        //AMQP_REQUEUE 可以重新投回队列
       $queue->nack($envelope->getDeliveryTag(),AMQP_REQUEUE);
       // $queue->reject($envelope->getDeliveryTag(),AMQP_REQUEUE);
    }

}
//设置消息队列消费者回调方法,并进行阻塞
$q->consume("receive");
//$q->consume("receive", AMQP_AUTOACK);//隐式确认,不推荐

2.生产者

<?php
/**
 * Created by PhpStorm.
 * User: jmsite.cn
 * Date: 2019/1/15
 * Time: 13:15
 */

$config = array(
    'host' => '192.168.6.210',
    'vhost' => '/',
    'port' => 5672,
//    'login' => 'test',
//    'password' => 'test'
);
$cnn = new AMQPConnection($config);
if (!$cnn->connect()) {
    echo "Cannot connect to the broker";
    exit();
}
$ch = new AMQPChannel($cnn);
$ex = new AMQPExchange($ch);
//消息的路由键,一定要和消费者端一致
$routingKey = 'key_1';
//交换机名称,一定要和消费者端一致,
$exchangeName = 'exchange_1';
$ex->setName($exchangeName);
$ex->setType(AMQP_EX_TYPE_DIRECT);
$ex->setFlags(AMQP_DURABLE);
$ex->declareExchange();
//创建10个消息
for ($i=1;$i<=10;$i++){
 //   sleep(1);
    //消息内容
    $msg = array(
        'data'  => 'message_'.$i,
        'hello' => 'world',
    );
    //发送消息到交换机,并返回发送结果
    //delivery_mode:2声明消息持久,持久的队列+持久的消息在RabbitMQ重启后才不会丢失
    echo "Send Message:".$ex->publish(json_encode($msg), $routingKey, AMQP_NOPARAM, array('delivery_mode' => 2))."\n";
    //代码执行完毕后进程会自动退出
}

在这里插入图片描述
执行过程可以看见如果重新投回队列的优先级是最高的
默认如果有3次没有回应(既没有ack也没有nack),那么消费者不再能获取消息

3.ACK消息确认机制

首先RabbitMQ支持消息确认机制来本证消息被consumer正常处理,当然也可以通过no-ack不使用确认机制。RabbitMQ默认是使用ACK确认机制的。当Consumer接收到RabbitMQ发布的消息时需要在适当的时机发送一个ACK确认的包来告知RabbitMQ,自己接收到了消息并成功处理。所以前面讲到适当的时机建议是在处理完消息任务后发送。正如我们之前的代码。

    $msg = $envelope->getBody();
    sleep(1);  //sleep1秒模拟任务处理
    echo $msg."\n"; //处理消息
    $q->ack($envelope->getDeliveryTag()); //手动发送ACK应答

那如果不发送会怎样呢?

在RabbitMQ中有一个prefetch_count的概念,这个参数的意思是允许Consumer最多同时处理几个任务。我的版本的RabbitMQ默认这个参数是3,也就是说如果某一个Consumer在收到消息后没有发送ACK确认包,RabbitMQ就会任务Consumer还在处理任务,当有3个消息都没有发送ACK确认包时,RabbitMQ就不会再发送消息给该Consumer。

    $msg = $envelope->getBody();
    sleep(1);  //sleep1秒模拟任务处理
    echo $msg."\n"; //处理消息
    //$q->ack($envelope->getDeliveryTag()); //手动发送ACK应答

3.对于死信队列的里面什么情况下才会丢给死信交换机

1,消息被拒绝(Basic.Reject/Basic.Nack) ,井且设置requeue 参数为false
2,消息过期
3,队列达到最大长度
4.当消息在一个队列中变成了死信消息后,可以被发送到另一个交换机,这个交换机就是DLX,绑定DLX的队列成为死信队列。当这个队列中存在死信时, RabbitMQ 就会立即自动地将这个消息重新发布到设置的DLX 上去,进而被路由到绑定该DLX的死信队列上。可以监听这个队列中的消息、以进行相应的处理,这个特性与将消息的TTL 设置为0 配合使用可以弥补imrnediate 参数的功能

这里需要注意的是,你在监听正常消费的设置死信的队列的时候,即使设置的时间到了也是不会丢给死信队列的,如果你不开启正常消费队列的监听,这个设置了死信的队列就成了延迟队列的效果,再次强调 理解概念

也可以用rabbitmq-delayed-message-exchange插件实现延迟功能

4.生成者确认模式

  1. 在channel上开启确认模式:$channel->confirm_select();
  2. 在channel上添加监听:$channel->wait_for_pending_acks();监听成功和失败的返回结果,根据具体的结果对
    消息进行重新发送、或记录日志等后续处理。

上述使用php-amqp


//创建10个消息
for ($i=1;$i<=10;$i++){
        //   sleep(1);
        //消息内容
        $msg = array(
            'data'  => 'message_'.$i,
            'hello' => 'world',
        );
        //发送消息到交换机,并返回发送结果
        //delivery_mode:2声明消息持久,持久的队列+持久的消息在RabbitMQ重启后才不会丢失
        echo "Send Message:".$ex->publish(json_encode($msg), $routingKey, AMQP_NOPARAM, array('delivery_mode' => 2))."\n";

}

$ack_call = function ($delivery_tag,$multiple){
            echo 'Message acked' .  PHP_EOL;
            return true;
        };

$nack_call = function ($delivery_tag,$multiple,$requeue){
            echo 'Message nacked' .  PHP_EOL;
        };


$ch->setConfirmCallback($ack_call,$nack_call);//设置ack和nack的回调逻辑
$ch->waitForConfirm(3); //在setConfirmCallback后使用

但是发现处理完ack或者nack逻辑后,waitForConfirm依旧一直阻塞,如果设置waitForConfirm(3) 表示3秒后会报错超时,只能设置为0作为进程一直阻塞调用,查询很多资料发现

PECL AMQP库和纯php-amqplib库都没有实现Confirms扩展

因此采用另一个方式,composer的扩展

-vvv能查看具体进度

composer require -vvv php-amqplib/php-amqplib
$routeKey = 'route';
$exchangeName = 'exchange_1';
$queueName = 'queue';

//连接mq server
$connection = new AMQPStreamConnection("192.168.6.210", 5672,'guest','guest','/');  //连接server
$channel = $connection->channel(); //创建通道
//推送成功
$channel->set_ack_handler(
    function (AMQPMessage $message) {
        echo "发送成功: " . $message->body . PHP_EOL;
    }
);

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


/*
 * bring the channel into publish confirm mode.
 * if you would call $ch->tx_select() before or after you brought the channel into this mode
 * the next call to $ch->wait() would result in an exception as the publish confirm mode and transactions
 * are mutually exclusive
 */
/*
 * 进入发布确认模式。
 * 如果在将通道引入此模式之前或之后调用$ch->tx_select()
 * 下一个调用$ch->wait()将导致发布确认模式和事务异常
 * 是互斥的
 */
$channel->confirm_select(); // 发布确认模式

// 通道
$channel->exchange_declare($exchangeName, 'direct', false, false, false);
// 队列
$channel->queue_declare($queueName, false, false, false, false);

// 使用routeKey绑定交换机和队列
$channel->queue_bind($queueName, $exchangeName, $routeKey);

//$channel->wait_for_pending_acks();


for ($i = 0; $i < 10; $i++) {
    $msg = new AMQPMessage('Hello World!'.$i);
    $channel->basic_publish($msg, $exchangeName, $routeKey);
    sleep(1);
}

/*
* you do not have to wait for pending acks after each message sent. in fact it will be much more efficient
* to wait for as many messages to be acked as possible.
*/
/*
 *您不必在每条消息发送后等待挂起的acks。事实上,这样会更有效率
 *等待尽可能多的邮件被屏蔽。
 */
$channel->wait_for_pending_acks(5);
// 监听成功或失败返回结束 成功/失败 => set_ack_handler/set_nack_handler

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

这个方式wait_for_pending_acks测试后发现不再阻塞下面的代码执行,能实现异步获取生成者的ack确认消息

链接地址:https://www.cnblogs.com/-mrl/p/11114116.html
https://blog.51cto.com/chinalx1/2150793
https://blog.csdn.net/qq_42724459/article/details/109489966

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ是一个开源的消息队列中间件,可以用于实现异步处理。下面是一种常见的使用RabbitMQ实现异步处理的方法: 1. 安装RabbitMQ:首先,你需要安装RabbitMQ并启动它。 2. 创建消息生产者:在应用程序中,你需要创建一个消息生产者,负责将需要异步处理的任务封装成消息并发送到RabbitMQ。 3. 创建消息消费者:接下来,你需要创建一个或多个消息消费者,负责从RabbitMQ接收消息并进行异步处理。这些消费者可以运行在单独的进程或服务器上。 4. 定义消息格式:你需要定义消息的格式,包括消息的内容和其他相关信息。例如,你可以使用JSON格式来定义消息。 5. 发送消息:消息生产者通过RabbitMQ的API将消息发送到指定的队列。你可以指定队列的名称和其他属性。 6. 接收消息:消息消费者通过订阅相应的队列来接收消息。当有新消息到达时,消费者会自动从队列中获取并处理消息。 7. 异步处理:消息消费者接收到消息后,可以根据消息的内容进行相应的异步处理。例如,可以将任务放入线程池或使用其他异步处理机制来执行任务。 8. 确认消息:当消息成功处理后,消费者需要向RabbitMQ发送确认消息,告知RabbitMQ该消息已经被处理。这样,RabbitMQ可以从队列中删除该消息。 9. 错误处理:在异步处理过程中,可能会出现错误。你需要对错误进行适当的处理,例如记录错误日志或发送通知。 通过使用RabbitMQ,你可以实现高效可靠的异步处理,提高应用程序的性能和可扩展性。同时,RabbitMQ还提供了许多其他功能,如消息持久化、消息优先级等,可以根据具体需求进行配置和使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值