原文:(六)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. hash_set和hash_map

    1.hash_set集合容器 hash_set利用链式哈希表,进行数据的插入.删除和搜索.与set容器同样,不同意插入反复键值的元素.SGIC++哈希表是一个链式的结构,由表头和一系列单链组成.表头是 ...

  2. POJ 1442 Black Box treap求区间第k大

    题目来源:POJ 1442 Black Box 题意:输入xi 输出前xi个数的第i大的数 思路:试了下自己的treap模版 #include <cstdio> #include < ...

  3. BOM 请给javascript一个说法-------Day33

    楼市低迷,业主是不是该要个说法.黄金暴跌,谁来给大妈们一个说法.中国足球,敢不敢给大家一个说法. 给个说法,谁给,给谁,这该是哲学的范畴了吧. 可是,在这里.BOM是真真切切的给javascript一 ...

  4. android图像处理(3)底片效果

    这篇将讲到图片特效处理的底片效果.跟前面一样是对像素点进行处理,算法是通用的. 算法原理:将当前像素点的RGB值分别与255之差后的值作为当前点的RGB值. 例: ABC 求B点的底片效果: B.r ...

  5. 57.大数据线性处理csdn数据(fread,fwrite) 百万数据秒读数据

    创建结构体存储csdn数据 struct csdn { ]; ]; ]; }; 对于分配的大小要先获取最大的长度,定义如下 //姓名最大长度 ; //密码最大长度 ; //邮件最大长度 ; //获取各 ...

  6. Android线程间通讯的几种方式

    1.runOnUiThread(Runnable)              在子线程中直接使用该方法,可以更新UI runOnUiThread(new Runnable(){//更新UI       ...

  7. HDU 2988 Dark roads(kruskal模板题)

    Dark roads Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  8. AJAX - 封装AJAX GET 数组join( )方法 键值对取value POST请求参数注意点

    function objToStr(obj){ obj.t = new Date().getTime(); // 给obj动态增加了一个属性 // 这个给对象添加属性的方法, 会被直接加到键值对里?? ...

  9. word2vec源代码解析之word2vec.c

    word2vec源代码解析之word2vec.c 近期研究了一下google的开源项目word2vector,http://code.google.com/p/word2vec/. 事实上这玩意算是神 ...

  10. 51 nod 1189 阶乘分数

    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1189 题目思路: 1/n! = 1/x +1/y ==> ...