RabbitMQ是常见的消息中间件。也许是还是不够了解的缘故,感觉功能还好吧。

讲到队列,大家脑子里第一印象是下边这样的。

P生产者推送消息-->队列-->C消费者取出消息

结构很简单,但是RabbitMQ应该是为了丰富的功能吧,把“队列”拆分了。

分成了:exchange(交换机)和queue(队列)两个部分

同时说明:

生产者推送消息只推到exchange,不知道会进入哪个queue。

exchange通过一个route key与queue绑定,这时才会知道消息具体落到了哪个queue里。

而消费则获取消息,是直接从队列里取的。

大概就是以上这个意思,问题是还有两个特殊的说明:

1.消费者是无法订阅或者获取不存在的MessageQueue中信息。

2.消息被Exchange接受以后,如果没有匹配的Queue,则会被丢弃。

简单的理解就是,如果exchange和queue不绑定,生产者推送的消息到exchange会直接丢弃(丢失),同时consume也无法完成订阅。

所以,这里就有一个问题,无论是推送消息进队列,还是订阅消息消费,必须先定义好exchange和queue并通过route key绑定一起。

那么到底是推送消息的时候定义并绑定呢?还是订阅的时候定义并绑定呢?

根据那两个特殊的说明理解,无论是谁定义绑定,都有可能会出现问题。

所以,最终就是推送和消费之前,都尝试定义exchange和queue,并完成绑定。

推送消息,相对简单,就一个publish指定exchange和route key就完成了。

消费消息,相对的复杂一点,有两种方式:

1、推送(push)订阅方式,使用consume方法订阅队列,只要队列有消息就消费

2、拉取(poll)主动拉取,使用get方法,主动去从队列一次拉取一条消息

这两种情况,都有各自的应用场景,可以根据需要自行选择。

额外提醒一点:尽量不要使用循环的方式调用get方法消费队列,尤其是处理的消息很多的情况。

如果大量的消费队列,建议直接使用consume方法。

还有一个情况,当消费者取出消息时,可以不对消息队列做任何操作,也可以将取出的消息删除。

毕竟,队列里的消息,消费后是需要删除的,取到消息,发给队列一个然亏,队列就删除该消息。

这个取出消息后的删除,也分两种情况:

1、一种是取出就删除,consume和get两个方法都有一个参数AMQP_AUTOACK自动反馈

2、另一种是取出后,并不会自动返回删除,而是将取出来的消息处理之后确认没有问题了,手动反馈给消息队列

至于选择哪种反馈方式,根据需求自行选择。

关于RabbitMQ还有很多要说的,比如一个exchange可以绑定多个queue,多个exchange可以绑定一个queue(多对多的关系)

还可以根据exchange不同的模式,搭配不同的route key做不同的匹配。各种组合吧。

灵活应用起来功能还是很强大的,只是具体使用时需要仔细,因为一个不小心,不是丢失消息就是多出很多消息。

所知有限,在此不做特复杂的说明,下边的例子也是一个简单的完成一个简单的队列的操作。演示学习用。

更多的情况,就自行研究扩展。

RabbitMQ操作类(rabbitmq.class.php)

 <?php
// rabbitmq 操作类
class RabbitMQ
{
// 配置变量
public $configs = array(
'host' => 'localhost',
'port' => '5672',
'login' => 'guest',
'password' => 'guest',
'vhost' => '/'
);
public $exchange_name = 'ex_q_def';// 交换机名称
public $queue_name = 'ex_q_def';// 队列名称
public $route_key = '';// 路由key的名称
public $durable = true;// 持久化,默认true
public $autodelete = false;// 自动删除 // 内部通用变量
private $_conn = null;
private $_exchange = null;
private $_channel = null;
private $_queue = null; // 构造函数
public function __construct()
{
// 初始化队列
$this->init();
} // 配置rabbitmq
public function set_configs($configs)
{
// 初始化配置
if (!is_array($configs)) {
echo 'configs is not array.';
}
if (!($configs['host'] && $configs['port'] && $configs['login'] && $configs['password'])) {
echo 'configs is empty.';
}
if (!isset($configs['vhost'])) {// 没有vhost元素,给出默认值
$configs['vhost'] = '/';
} else {
if (empty($configs['vhost'])) {// 有vhost元素,但是值为空,给出默认值
$configs['vhost'] = '/';
}
}
$this->configs = $configs;
} // 初始化rabbitmq
private function init()
{
if (!$this->_conn) {
$this->_conn = new AMQPConnection($this->configs);// 创建连接对象
if (!$this->_conn->connect()) {
echo "Cannot connect to the broker \n ";
exit(0);
}
} // 创建channel
$this->_channel = new AMQPChannel($this->_conn);
} // 创建队列(为了保证正常订阅,避免消息丢失,生产者和消费则都要尝试创建队列:交换机和队列通过路由绑定一起)
public function create_queue($exchange_name='', $route_key='', $queue_name='')
{
if ($exchange_name != '') {
// 队列名参数可以省略,默认与交换机同名
$this->exchange_name = $exchange_name;// 更新交换机名称
$this->queue_name = $exchange_name;// 更新队列名称
}
if ($route_key != '') $this->route_key = $route_key;// 更新路由
if ($queue_name != '') $this->queue_name = $queue_name;// 独立更新队列名称 // 创建exchange交换机
$this->_exchange = new AMQPExchange($this->_channel);// 创建交换机
$this->_exchange->setType(AMQP_EX_TYPE_DIRECT);// 设置交换机模式为direct
if ($this->durable) {
$this->_exchange->setFlags(AMQP_DURABLE);// 设置是否持久化
}
if ($this->autodelete) {
$this->_exchange->setFlags(AMQP_AUTODELETE);// 设置是否自动删除
}
$this->_exchange->setName($this->exchange_name);// 设置交换机名称
$this->_exchange->declare(); // 创建queue队列
$this->_queue = new AMQPQueue($this->_channel);
if ($this->durable) {
$this->_queue->setFlags(AMQP_DURABLE);// 设置是否持久化
}
if ($this->autodelete) {
$this->_queue->setFlags(AMQP_AUTODELETE);// 设置是否自动删除
}
$this->_queue->setName($this->queue_name);// 设置队列名称
$this->_queue->declare();// 完成队列的定义 // 将queue和exchange通过route_key绑定在一起
$this->_queue->bind($this->exchange_name, $this->route_key);
} // 生产者,向队列交换机发送消息
public function send($msg, $exchange_name='', $route_key='', $queue_name='')
{
$this->create_queue($exchange_name, $route_key, $queue_name);// 创建exchange和queue根据route_key绑定一起
// 消息处理
if (is_array($msg)) {
$msg = json_encode($msg);// 将数组类型转换成JSON格式
} else {
$msg = trim(strval($msg));// 简单处理一下要发送的消息内容
} // 生产者推送消息进队列时,只能将消息推送到交换机exchange中
if ($this->durable) {
$this->_exchange->publish($msg, $this->route_key, AMQP_NOPARAM, array('delivery_mode'=>2));// delivery_mode 2持久化 1非持久化;AMQP_NOPARAM表示无参数
} else {
$this->_exchange->publish($msg, $this->route_key);
}
} // 消费者,从队列中获取数,消费队列(订阅)
public function run($fun_name, $exchange_name='', $route_key='', $queue_name='', $autoack=false)
{
if (!$fun_name) return false;// 没有返回函数,或者队列不存在
$this->create_queue($exchange_name, $route_key, $queue_name);
// 订阅消息
while (true) {
if ($autoack) {
$this->_queue->consume($fun_name, AMQP_AUTOACK);// 自动应答
} else {
$this->_queue->consume($fun_name);// 需要手动应答
}
}
} // 消费者,从队列中获取数,消费队列(主动获取)
public function get($exchange_name='', $route_key='', $queue_name='', $autoack=false)
{
$this->create_queue($exchange_name, $route_key, $queue_name);
// 主动获取消息
if ($autoack) {
$msg = $this->_queue->get(AMQP_AUTOACK);// 自动应答
} else {
$msg = $this->_queue->get();// 需要手动应答
}
return ['msg'=>$msg, 'queue'=>$this->_queue];
}
}
?>

生产者推送(test_send.php)

 <?php
require_once('rabbitmq.class.php'); $rmq = new RabbitMQ;
for ($i = 0; $i < 10; $i++) {
echo 'test_consume_' . $i .'<br />';
$rmq->send('test_consume_' . $i, 'test_consume');
} for ($i = 0; $i < 10; $i++) {
echo 'get_msg_'.$i.'<br />';
$rmq->send('get_msg_' . $i, 'test_get');
} echo 'send ok ! ' . date('Y-m-d H:i:s');
?>

消费者consume(test_run.php)

 <?php
require_once('rabbitmq.class.php'); $rmq = new RabbitMQ; $s = $rmq->run('processMessage', 'test_consume'); function processMessage($envelope, $queue) {
$msg = $envelope->getBody();
sleep(1); //sleep1秒模拟任务处理
echo $msg."\n"; //处理消息
$queue->ack($envelope->getDeliveryTag()); //手动发送ACK应答
}
?>

消费者get(test_get.php)

 <?php
require_once('rabbitmq.class.php'); $rmq = new RabbitMQ; $r = $rmq->get('test_get'); echo $r['msg']->getBody();// 取到的消息
$r['queue']->ack($r['msg']->getDeliveryTag());// 手动反馈,删除消费的消息
?>

订阅consume可以起多个程序,队列会轮询平均的分到每一个订阅里。当然,前提是处理速度是一样,并且都有反馈。

如果处理速度不同,哪个快 ,哪就会分配更多的消息。如果没有反馈,默认只会推送3条消息,如果一直不给反馈,就不会再有推送了。

此时如果中断这个没有反馈的订阅,因为队列中没有删除,会再次分配到其他订阅者哪里继续推送消费。

同样的,如果队列中有消息,随时开启新的订阅,随时就会分配到消费的消息。

PHP操作RabbitMQ的类 exchange、queue、route kye、bind的更多相关文章

  1. RabbitMq的整理 exchange、route、queue关系

    https://blog.csdn.net/samxx8/article/details/47417133

  2. php 操作RabbitMQ

    本文摘抄自:https://www.cnblogs.com/alin-qu/p/8312874.html php 操作RabbitMQ   基本流程图 如果exchange 没有绑定queue,则消息 ...

  3. Python 【第六章】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  4. Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  5. Python之路【第九篇】:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy   Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用 ...

  6. 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ

    本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...

  7. Python 之路:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    一.Memcached Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负债.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速 ...

  8. Python之路:Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

  9. 【转】Python操作 RabbitMQ、Redis、Memcache、SQLAlchemy

    Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度 ...

随机推荐

  1. javascript原型模式概念解读

    原型模式(prototype)是指用原型实例指向创建对象的种类,并且通过拷贝这些原型创建新的对象.对于原型模式,可以利用JavaScript特有的原型继承特性去创建对象的方式,真正的原型继承是作为最新 ...

  2. python3 参数*args 、 **args 在函数定义和调用中的应用

    一.函数调用时 说明:*args 表示解包(解包 列表.元组.字符串类型) #定义函数cn_musql def cn_musql(host,port,user,pwd,db): print(host) ...

  3. PHP编译报错

    //usr/lib64/liblber-2.4.so.2: error adding symbols: DSO missing from command line collect2: error: l ...

  4. python 科学计算与可视化

    一.Numpy 库 NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. 引用: import ...

  5. [Day18]集合框架Collection、迭代器、增强for循环以及泛型

    1.集合 1.1集合-本身是一个存储的容器 集合类的基本接口是Collection接口,这个接口有两个基本方法 (1)boolean add(E element) 用于向集合中添加元素,如果添加元素确 ...

  6. VisualStudioCode创建的asp.net core控制台程序部署到linux

    1.asp.net core控制台程序 static void Main(string[] args) { ; ) { Console.WriteLine("Hello World!&quo ...

  7. MySQL中0、'0'作为条件时的区别

    一.现象: 今天查询时,某字段x为varchar 可能的值为A B C 0 1 2 3 4 想把0 1 2 3 4的所有数据查出,使用的SQL为 select * from table where x ...

  8. Redis 开发规范

    本文主要介绍在使用阿里云Redis的开发规范,从下面几个方面进行说明. 键值设计 命令使用 客户端使用 相关工具 通过本文的介绍可以减少使用Redis过程带来的问题. 一.键值设计 1.key名设计 ...

  9. npm手册

    npm现在都是随同NodeJS一起安装的包管理和分发工具,所以npm的安装,只要下载新版的nodejs已经集成了npm. 安装好了以后,直接输入npm或者npm help,会出来一些npm自带的命令. ...

  10. 个人常用的移动端浅灰底index.html

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...