原文:(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版)

在前面一章介绍了在PHP中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消费者消费同一个队列怎么办?如果这几个消费者分任务的权重不同怎么办?怎么把同一个队列不同级别的任务分发给不同的消费者?如果消费者异常离线怎么办?不要着急,后面将慢慢解开面纱。我们将结合实际的应用场景来讲解更多的高级用法。

任务分发机制

设想如果把每个消息当做一个任务,生产者把任务发布到RabbitMQ,然后Consumer接收消息处理任务,如果我们发现一个Consumer不能完成任务处理怎么办呢,我们会增加Consumer的数量。由一个Consumer增加到两个Consumer,如图由C变为C1和C2共同来分单工作。如果C1和C2是完全一样的,那RabbitMQ会将任务平均分发到两个消费者。

如下我们新建c1.php和c2.php来订阅同一个队列在接收到消息后sleep1秒模拟任务处理的时间。

p.php代码,生产100条带编号的消息:

<?php

/*
* 发布-订阅-P
* create by superrd
*/ $queueName = 'superrd';
$exchangeName = 'superrd';
$routeKey = 'superrd';
$message = 'task--';
$connection = new AMQPConnection(array('host' => '10.99.121.137', 'port' => '5672', 'vhost' => '/', 'login' => 'superrd', 'password' => 'superrd'));
$connection->connect() or die("Cannot connect to the broker!\n");
try {
$channel = new AMQPChannel($connection);
$exchange = new AMQPExchange($channel);
$exchange->setName($exchangeName);
$exchange->setType(AMQP_EX_TYPE_DIRECT);
$exchange->setFlags(AMQP_DURABLE);
$exchange->declareExchange(); $queue = new AMQPQueue($channel);
$queue->setName($queueName);
$queue->setFlags(AMQP_DURABLE);
$queue->declareQueue(); $queue->bind($exchangeName, $routeKey); for($i=0 ; $i<100;$i++){
$exchange->publish($message.$i,$routeKey);
var_dump("[x] Sent $message $i");
}
} catch (AMQPConnectionException $e) {
var_dump($e);
exit();
}
$connection->disconnect();

c1.php和c2.php代码完全一样:

<?php

/*
* 发布-订阅-c1c2
* create by superrd
*/ $queueName = 'superrd';
$exchangeName = 'superrd'; $connection->connect() or die("Cannot connect to the broker!\n");
$channel = new AMQPChannel($connection);
$exchange = new AMQPExchange($channel);
$exchange->setName($exchangeName);
$exchange->setType(AMQP_EX_TYPE_DIRECT);
$exchange->setFlags(AMQP_DURABLE);
$exchange->declareExchange(); $queue = new AMQPQueue($channel);
$queue->setName($queueName);
$queue->setFlags(AMQP_DURABLE);
$queue->declareQueue(); $queue->bind($exchangeName, $routeKey);
//阻塞模式接收消息 echo "Message:\n";
while(True){
$queue->consume('processMessage');
//自动ACK应答
//$queue->consume('processMessage', AMQP_AUTOACK);
} $conn->disconnect();
/*
* 消费回调函数
* 处理消息
*/
function processMessage($envelope, $q) {
$msg = $envelope->getBody();
sleep(1); //sleep1秒模拟任务处理
echo $msg."\n"; //处理消息
$q->ack($envelope->getDeliveryTag()); //手动发送ACK应答
}

打开两个中断窗口分别执行c1.php和c2.php脚本。确定两个脚本处于订阅状态,然后执行p.php脚本。

看到上面两幅图结果就一目了然了。因为两个脚本sleep的时间相同所以任务是完全平均分发到两个消费者的。我们修改下c2.php脚本的sleep时间为2秒,看下结果会怎么样。

可以看到c1.php脚本共收到66条消息,c2.php脚本收到34条消息,基本是按照2:1来分配。那RabbitMQ是如何来保证这样的分发机制呢,下面看RabbitMQ是如何通过ACK确认机制来实现任务分发的。

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。

我们把c2.php的sleep时间改回1秒,并且注释掉ACK确认。

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

发现c2脚本只收到三条消息。通过WEB管理工具也可以看到有三条消息是没有被ACK确认的。

当然任务并不会一直卡在这里,在这是RabbitMQ任务c2在处理这三个任务。如果c2忽然终止RabbitMQ会重新分发任务。如下我终止c2脚本。

三条任务被重新分发到了c1。再查看下WEB管理工具,unackd已经为0

如果Consumer数量很多或者希望每个Consumer同时只处理一个任务可以通过在Consumer中设置PrefetchCount来实现更加均匀的任务分发。

$channel = new AMQPChannel($connection);
$channel->setPrefetchCount(1);

如下我修改了c2的PrefetchCount为1。在WEB管理插件中可以看到已经有一个Consumer的PrefetchCount为1了。

RabbitMQ技术交流QQ群:327034977(添加时请备注RabbitMQ)

(六)RabbitMQ消息队列-消息任务分发与消息ACK确认机制(PHP版)的更多相关文章

  1. RabbitMQ消息队列(六)-消息任务分发与消息ACK确认机制(.Net Core版)

    在前面一章介绍了在.Net Core中如何使用RabbitMQ,至此入门的的部分就完成了,我们内心中一定还有很多疑问:如果多个消费者消费同一个队列怎么办?如果这几个消费者分任务的权重不同怎么办?怎么把 ...

  2. (七)RabbitMQ消息队列-通过fanout模式将消息推送到多个Queue中

    原文:(七)RabbitMQ消息队列-通过fanout模式将消息推送到多个Queue中 前面第六章我们使用的是direct直连模式来进行消息投递和分发.本章将介绍如何使用fanout模式将消息推送到多 ...

  3. (五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版)

    原文:(五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版) 本文将介绍在PHP中如何使用RabbitMQ来实现消息的订阅和发布.我使用的系统依然是Centos7,为了方便, ...

  4. 消息队列一:为什么需要消息队列(MQ)?

    为什么会需要消息队列(MQ)? #################################################################################### ...

  5. 使用kafka消息队列解决分布式事务(可靠消息最终一致性方案-本地消息服务)

    微服务框架Spring Cloud介绍 Part1: 使用事件和消息队列实现分布式事务 本文转自:http://skaka.me/blog/2016/04/21/springcloud1/ 不同于单一 ...

  6. 消息队列:快速上手ActiveMQ消息队列的JMS方式使用(两种模式:Topic和Queue的消息推送和订阅)

    1.实现功能 希望使用一套API,实现两种模式下的消息发送和接收功能,方便业务程序调用 1.发送Topic 2.发送Queue 3.接收Topic 4.接收Queue 2.接口设计 根据功能设计公共调 ...

  7. Azure Messaging-ServiceBus Messaging消息队列技术系列5-重复消息:at-least-once at-most-once

    上篇博客中,我们用实际的业务场景和代码示例了Azure Messaging-ServiceBus Messaging对复杂对象消息的支持和消息的持久化: Azure Messaging-Service ...

  8. MQ消息队列(2)—— Java消息服务接口(JMS)

    一.理解JMS   1.什么是JMS?         JMS即Java消息服务(Java Message Service)应用程序接口,API是一个消息服务的标准或者说是规范,允许应用程序组件基于J ...

  9. RabbitMQ (十六) 消息队列的应用场景 (转)

    原贴 : http://blog.csdn.net/cws1214/article/details/52922267 消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题 ...

随机推荐

  1. JNDI学习总结(2)——JNDI数据源的配置

    一.数据源的由来 在Java开发中,使用JDBC操作数据库的四个步骤如下:   ①加载数据库驱动程序(Class.forName("数据库驱动类");)    ②连接数据库(Con ...

  2. ActiveMQ学习总结(7)——ActiveMQ使用场景

    MQ简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们.消息传 ...

  3. 使用Cygwin在Windows上体验Linux的快感

    前言 记得大学的时候就以前使用过Cygwin,可惜当时没有发现她的美,我相信如今大多数朋友可能会更加倾向于使用Git或者干脆直接使用虚拟机以及原生Unix. 只是对于刚进入Linux的世界新人来说,使 ...

  4. js06--利用js给数组去重

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/stri ...

  5. Android开发经验小知识点

    <1> 设置屏幕无标题栏 在AndroidManifest.xml设置: Android:theme="@android:style/Theme.NoTitleBar"

  6. 随时查看源码的网站---http://www.sooset.com/

    由于工作需要经常要在Windows平台下参阅linux源码,以前都用http://lxr.linux.no/来浏览源码(如下图所示),最近发现sooset来浏览更方便,所以介绍给大家分享. 650) ...

  7. thinkphp动态注册路由

    thinkphp动态注册路由 一.总结 1.thinkphp使用路由步骤:a.config配置文件中开启路由  b.Route类的rule方法创建路由(在Routephp中)Route::rule(' ...

  8. 15.lambda表达式

    #include <iostream> #include <array> using namespace std; //解决函数怀孕现象 //[](){} //[] =引用,只 ...

  9. JS 数字格式千分位相互转换

    /** * 将数值四舍五入后格式化. * * @param num 数值(Number或者String) * @param cent 要保留的小数位(Number) * @param isThousa ...

  10. 通达OA二次开发 对通达2015版微信查询用户信息模块升级开发(图文)

    OA提供对微信的支持这一点做的很好,用户使用起来也更方便了. 而当中的个别功能还有待完好,比现在天要说的这个微信查询用户信息模块. 升级前的用法:输入@+用户中文名.而且要求全然匹配,然而在实际使用中 ...