php 使用 rabbitmq 工作模式 work_queue(二)

这里来说 php 使用rabbmitmq 的工作模式 work_queue
work_queue的示意图是这样子的
在这里插入图片描述
也就是说 有一个生产者, 有两个或多个消费者在消费队列, 这些消费者之间的关系是竞争的, 也就是说一个队列消息, 只会有一个消费者使用
这个模式, 从图上来看, 和简单工作模式差不多, 无非就是多了一个 消费者, 所以,它的代码如下

1.生产者代码

work_queue_sender.php

<?php

    require __DIR__."/../vendor/autoload.php";

    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection("127.0.0.1",5672,"admin","123456");
    $channel = $connection->channel();
    $channel->queue_declare("work_queue",false,false,false,false);
    //因为有两个消费者, 我们这里就使用循环多生产一些消息
    for($i=0;$i<10;$i++){
        $msg = new PhpAmqpLib\Message\AMQPMessage($i."----这是生产者生产的第--".$i."--条消息");
        $channel->basic_publish($msg,"","work_queue");
    }
    $channel->close();
    $connection->close();

2.消费者1代码

<?php
    require __DIR__."/../vendor/autoload.php";

    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('127.0.0.1',5672,"admin","123456");
    $channel = $connection->channel();
    $channel->queue_declare("work_queue",false,false,false,false);
    $callback = function($msg){
        echo "这是消费者1 消费的消息 消息内容是:".$msg->body.PHP_EOL;
    };
    //$channel->basic_consume("work_queue","",false,true,false,false,$callback);
    $channel->basic_consume("work_queue","",false,false,false,false,$callback);
    while(count($channel->callbacks)){
        $channel->wait();
    }

3.消费者2代码

    require __DIR__."/../vendor/autoload.php";

    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('127.0.0.1',5672,"admin","123456");
    $channel = $connection->channel();
    $channel->queue_declare("work_queue",false,false,false,false);
    $callback = function($msg){
        echo "这是消费者1 消费的消息 消息内容是:".$msg->body.PHP_EOL;
    };
    //$channel->basic_consume("work_queue","",false,true,false,false,$callback);
    $channel->basic_consume("work_queue","",false,false,false,false,$callback);
    while(count($channel->callbacks)){
        $channel->wait();
    }

测试, 我们先开启两个消费者
在这里插入图片描述
在这里插入图片描述
再执行生产者

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
从上面可以看到 消费者是交替消费的


ack 机制

我们可以看到

$channel->basic_consume("work_queue","",false,false,false,false,$callback);

上面的代码中 basic_consume 的参数, 第四个是false , 它表示开启了消息确认 , 如果是true, 表示不开启消息确认
为什么有 ack机制, 当我们不开启消息确认机制的时候, 也就是说 , 当rabbmitmq 向消费息发送了消息之后, 就立即将这条消息从内存中清除,那如果消费者在处理这个消息的时候出现了错误, 导至程序停止了, 这条消息将会丢失,同时,已经发送给这个消费者的所有消息都将丢失, 这是我们不想看到的
那如果我们开启了消息确认, 消费者在处理消息后, 会向生产者返回消息, 这时消息才会被删除, 如果程序出现了错误中止, 生产者会检测到 tcp连接断开,就会把没有处理过的消息, 重新发送给还在运行的另一个消费者进程
这样就不会丢失任何消息了

# 处理消息回调函数  
//首先把第四个参数改成false 
//同时 callback中 添加
//$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
$callback = function($msg){
  echo " [x] Received ", $msg->body, "\n";
  sleep(substr_count($msg->body, '.'));
  echo " [x] Done", "\n";
  $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
# 开启消息确认
$channel->basic_consume('task_queue', '', false, false, false, false, $callback);

消息的持久化

我们已经学习了如何确保即使消费者异常退出后,任务也不会丢失。但是如果RabbitMQ服务器挂了,我们的任务任然会丢失。

当RabbitMQ退出或崩溃时,它会丢失队列和其中的消息,除非你告诉他要持久化这些信息。需要两件事来确保消息不会丢失:我们需要将队列和消息标记为 durable(持久化)。

首先,我们需要确保RabbitMQ不会丢失消息队列。为了做到这一点,我们需要将队列声明为持久化,只要将 queue_declare 的第三个参数设置为 true 就行了:

$channel->queue_declare('hello', false, true, false, false)

设置为 true 的标志需要同时应用于生产者和消费者中。
同时, 我们还要为我们的 消息设置 持久化标记

$msg = new AMQPMessage($data,
       array('delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT)
       );

delivery_mode’ => AMQPMessage::DELIVERY_MODE_PERSISTENT 就是把消息也加上了持久化标记

公平调度

RabbitmitMq 不会去看消息是否处理完成, 它只会盲目的把消息不断的发送给消费者, 这就会出现不平衡的问题, 有的消费者计算能力弱, 有的计算能力强, 此时就可以使用公平调度, 等一个消费者处理完消息之后, 再给它发送下一条消息
比如在两个消费者的情况下,当所有奇数序号的任务量都很大,并且偶数序号的任务量小的情况下,一个消费者将不断忙碌,另一个消费者几乎没有任何工作量。在这种情境下,RabbitMQ依然不知道什么信息,还是会将消息平均分配。
这种情况的发生是因为RabbitMQ只管分配进入队列的消息。它不会去看消费者的未确认消息的数量。它只是盲目地向第n个消费者发送第n条消息。
在这里插入图片描述

为了解决以上问题,我们可以通过设置 basic_qos 第二个参数 prefetch_count = 1。这一项告诉RabbitMQ不要一次给一个消费者发送多个消息。或者换一种说法,在确认前一个消息之前,不要向消费者发送新的消息。相反,新的消息将发送到一个处于空闲的消费者。

$channel->basic_qos(null, 1, null);

最终, 综合上面的, 我们的生产者代码这样的

<?php

    require __DIR__."/../vendor/autoload.php";

    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection("127.0.0.1",5672,"admin","123456");
    $channel = $connection->channel();
    //这是开启了     消息的持久化
    $channel->queue_declare("work_queue_last",false,true,false,false);
    //因为有两个消费者, 我们这里就使用循环多生产一些消息
    for($i=0;$i<10;$i++){
        $msg = new PhpAmqpLib\Message\AMQPMessage($i."----这是生产者生产的第--".$i."--条消息",
        ["delivery_mode"=>\PhpAmqpLib\Message\AMQPMessage::DELIVERY_MODE_PERSISTENT]);
        $channel->basic_publish($msg,"","work_queue_last");
    }
    $channel->close();
    $connection->close();

消费者的代码是这样的

<?php
    require __DIR__."/../vendor/autoload.php";

    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('127.0.0.1',5672,"admin","123456");
    $channel = $connection->channel();
    // 因为生产者中的  第三个参数为 true 消费者中对应的也要配成true
    $channel->queue_declare("work_queue_last",false,true,false,false);
    //这里开启 公平调机制
    $channel->basic_qos(null,1,null);
    $callback = function($msg){
        echo "这是消费者1 消费的消息 消息内容是:".$msg->body.PHP_EOL;
        //这里要手机的做 ack
        sleep(3);
        $msg->delivery_info['channel']->basic_ack($msg->delivery_info["delivery_tag"]);
    };
    //这里开启的应答 ack 机制
    $channel->basic_consume("work_queue_last","",false,true,false,false,$callback);
    //$channel->basic_consume("work_queue","",false,false,false,false,$callback);
    while(count($channel->callbacks)){
        $channel->wait();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

A黄俊辉A

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

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

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

打赏作者

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

抵扣说明:

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

余额充值