发布/订阅

我们在上一节创建了一个工作队列,并假定队列对应的任务传送给了某个客户端。在这一章节我们会做一些完全不一样的东西–我们会发送一条消息到多个消费者,也称之为“发布/订阅”模式。

为了说明这个模式,我们会创建一个简单的日志系统(logging system,以下简称日志系统),它由两个程序组成–第一个是发送日志信息,第二个是接收日志并打印。

日志系统的每一个运行的接收端程序都会接收信息,这样就可以运行一个接收端就把日志保存到硬盘里,同时运行另一个接收端去实时显示日志到屏幕。

本质上,日志内容是广播给所有的接收端的。

交换器

在之前的章节中我们从一个队列里发送和接收消息,现在该把完整的RabbitMQ消息模型介绍给大家了。

让我们快速的回看一遍在之前的章节中的内容:

>生产者是一个用来发送消息的程序

>队列是一个存储消息的缓冲区

>消费者是一个接收消息的程序

RabbitMQ消息模型的核心思想是,生产者永远不会直接发送给任何消息队列,实际上,生产者一般情况下甚至不知道消息应该发送给哪个队列。

生产者只能发送消息到交换器中,交换器非常简单。一方面从生产者接收消息,另一方面把消息推送到队列中。交换器必须知道如何处理接收到的消息,是推送到某个队列?推送到多个队列?还是丢弃这条消息。这个规则通过交换器类型(exchange type)来指定。

这里是交换器的几个类型:direct,topic,headers,fanout。这里我们主要关注最后一个–fanout,创建一个类型为 fanout 的交换器,命名为 logs。

$channel->exchange_declare('logs','fanout',false,false,false);

fanout交换器非常简单,你可以从名称中猜出它的功能,它把所有接收到的消息广播给所有它知道的队列,这也正是我们的日志系统需要的功能。

列出交换器

可以使用rabbitmqctl 命令列出服务器上的所有交换器:

sudo rabbitmqctl list_exchanges

Listing exchanges ...
direct
amq.direct direct
amq.fanout fanout
amq.headers headers
amq.match headers
amq.rabbitmq.log topic
amq.rabbitmq.trace topic
amq.topic topic
logs fanout
...done.

结果中有一些amq.*和一些未命名的交换器,这是一些默认创建的交换器,它们不太可能是现在需要用到的。

未命名交换器

在之前的章节中我们对交换器一无所知,直到可以发送消息给队列。大概是因为我们当时正在使用一个以空字符串“”定义的默认的交换器。

回想一下之前怎么发布消息:

$channel->basic_publish($msg,'','hello');

这里就是使用默认或者说未命名的交换器:消息被routing_key的值
Here we use the default or nameless exchange: messages are routed to the queue with the name specified by routing_key, if it exists. The routing key is the second argument to basic_publish

现在,可以发布消息到这个队列。

$channel->exchange_declare('logs','fanout',false,false,false);
$channel->basic_publish($msg,'logs');

临时队列

也许你还记得在之前我们使用了一个指定的队列(还记得 hello 队列 和 task_queue 队列吗?)。可以命名一个队列是至关重要的–我们需要指定一个worker到同一个队列。当想让生产者和消费者使用同一个队列时给队列命名是非常重要的。

但是在我们的日志系统中情况不同了,我们想要接收所有的消息,不仅仅是其中的一部分,我们关心的是最新的消息而不是旧的,因此需要做两件事。

首先,当连接到RabbitMQ时,需要一个空的队列,可以手动创建一个名字随机的队列,或者,更好的办法是,让服务器为我们随机选一个队列名字。

其次,一旦与消费者失去连接,队列需要自动删除。大专栏  PHP RabbitMQ 教程(三)p>

php-amqplib中,当我们创建了一个名字为空的队列时,实际上是创建了一个被生成了名字的非持久化的队列。

list($queue_name, ,) = $channel->queue_declare("");

方法执行后,$queue_name变量包含了一个RabbitMQ生成的字符串。比如也许是这样的:amq.gen-JzTY20BRgKO-HjmUJj0wLg。

当连接被关闭的时候,队列也会被删掉,因为队列是独有的。

绑定(Bindlings)

我们已经创建了一个fanout类型的交换器和一个队列。现在需要让交换器发送消息给队列。交换器和队列之间的关系称之为绑定(binding)

$channel->queue_bind($queue_name,'logs');

现在开始,logs 交换器会把消息附加到队列中。

列出绑定(Listing bindings)

可以使用 rabbitmqctl list_bindings列出所有存在的正在使用的绑定。

整合

发送日志消息的生产者,与之前的代码看起来没什么不同,最重要的变化是现在想要发送消息到我们的 logs 交换器中,需要在发送时提供一个routing_key,但是在 fanout类型的交换器中这个值是可以忽略的。下边是emit_log.php的代码。

<?php

require_once __DIR__ .'/verdor/autoload.php';
use PhpAmqpLibConnectionAMQPStreamConnection;
use PhpAmqpLibMessageAMQPMessage; $connection = new AMQPStreamConnection('localhost',5672,'guest','guest');
$channel = $channel->channel(); $channel->exchange_declare('logs','fanout',false,false,false); $data = implode(' ',array_slice($argv,1)); if(empty($data)) $data = "info:Hello World";
$msg = new AMQPMessage($data); $channel->basic_publish($msg,'logs'); echo "[x]Sent ",$data,"n"; $channel->close();
$connection->close();
?>

(emit_log.php)

如你所见,建立连接后声明了交换器,这一步是必须的,因为发送消息到一个不存在的交换器是被禁止的。

如果还没有队列绑定到交换器,信息会丢失,但是这对于我们是可以的,如果没有消费者监听,我们可以安全的丢弃消息。

receive_logs.php:

<?php

require_once __DIR__ .'/vendor/autoload.php';
use PhpAmqpLibConnectionQMAPStreamConnection; $connection = new AMQPStreamConnection('localhost',5672,'guest','guest');
$channel = $connection->channel(); $channel->queue_bind($queue_name,'logs'); echo '[*] Waiting for logs. To exit press CTRL+C',"n"; $callback = function($msg){
echo '[x]',$msg->body,"n";
}; $channel->basic_consume($queue_name,'',false,true,false,false,$callback); whild(count($channel->callbacks)){
$channel->wait();
} $channel->close();
$connection->close();
?>

(receive_logs.php)

如果想保存日志到文件中,可以在命令中输入

php receive_logs.php > logs_from_rabbit.log

如果想在屏幕上查看日志,新打开一个终端并运行:

php receive_logs.php

发送日志:

php emit_log.php

使用 rabbitmqctl list_bindings 可以确认代码确实创建了绑定和队列,当两个receive_logs.php在运行的时候会看到类似这样的:

sudo rabbitmqctl list_bindings
Listing bindings ...
logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
...done.

对于结果的解释很简单,logs交换器中的数据发送到两个服务器指定的队列,而这正是我们要实现的。

想要弄明白怎样去监听部分消息,转到第四部分。

原文地址:Publish/Subscribe

PHP RabbitMQ 教程(三)的更多相关文章

  1. RabbitMQ教程总结

    [译]RabbitMQ教程一 主要通过Hello Word对RabbitMQ有初步认识 [译]RabbitMQ教程二 工作队列,即一个生产者对多个消费者 循环分发.消息确认.消息持久.公平分发 [译] ...

  2. RabbitMQ官方教程三 Publish/Subscribe(GOLANG语言实现)

    RabbitMQ官方教程三 Publish/Subscribe(GOLANG语言实现) 在上一个教程中,我们创建了一个工作队列. 工作队列背后的假设是,每个任务都恰好交付给一个worker处理. 在这 ...

  3. RabbitMQ入门教程(三):Hello World

    原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...

  4. RabbitMQ(三) -- Publish/Subscribe

    RabbitMQ(三) -- Publish/Subscribe `rabbitmq`支持一对多的模式,一般称为发布/订阅.也就是说,生产者产生一条消息后,`rabbitmq`会把该消息分发给所有的消 ...

  5. RabbitMQ教程(一)——安装配置

    RabbitMQ教程(一)——安装配置 一.前言 由于最近在学习RabbitMQ消息队列,但是鉴于网上对于官网介绍的教程比较少或者由于时间长长期未更新,因此决定将对官网的RabbitMQ入门教程进行翻 ...

  6. 【详细】【转】C#中理解委托和事件 事件的本质其实就是委托 RabbitMQ英汉互翼(一),RabbitMQ, RabbitMQ教程, RabbitMQ入门

    [详细][转]C#中理解委托和事件   文章是很基础,但很实用,看了这篇文章,让我一下回到了2016年刚刚学委托的时候,故转之! 1.委托 委托类似于C++中的函数指针(一个指向内存位置的指针).委托 ...

  7. 保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java、Golang两种客户端教学Case)

    保姆级别的RabbitMQ教程!一看就懂!(有安装教程,送安装需要的依赖包,送Java.Golang两种客户端教学Case)   目录 什么是AMQP 和 JMS? 常见的MQ产品 安装RabbitM ...

  8. CRL快速开发框架系列教程三(更新数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  9. 手把手教从零开始在GitHub上使用Hexo搭建博客教程(三)-使用Travis自动部署Hexo(1)

    前言 前面两篇文章介绍了在github上使用hexo搭建博客的基本环境和hexo相关参数设置等. 基于目前,博客基本上是可以完美运行了. 但是,有一点是不太好,就是源码同步问题,如果在不同的电脑上写文 ...

随机推荐

  1. Python 学习笔记:Python 连接 SQL Server 报错(20009, b'DB-Lib error message 20009, severity 9)

    问题及场景: 最近需要使用 Python 将数据写到 SQL Server 数据库,但是在进行数据库连接操作时却报以下错误:(20009, b'DB-Lib error message 20009, ...

  2. Python常用模块小结

    目录 Python常用模块小结 一.Python常用模块小结 1.1 time模块 1.2 datetime模块 1.3 random模块 1.4 os模块 1.5 sys模块 1.6 json模块 ...

  3. Matlab高级教程_第二篇:Matlab相见恨晚的模块_02_并行运算-1

    1 更高级的算法牵扯到更多重的循环和复杂的计算,尤其是现在人工智能的算法尤其如此.有些历史知识的人能够了解到,人工智能的很多基本算法其实近百年之前就有了,但是当时的计算机技术达不到去实现这些算法的要求 ...

  4. RxJava的简单使用

    0x00 介绍 先简单介绍一下这个库,Rx的一系列实现都是为了解决同一个问题,就是让异步编程变的更加简单.它的主要思想是使用观察者模式,分离了数据源和数据的使用者,同时它拓展了观察者模式,将数据源中的 ...

  5. Django+ajax 返回json数据挨个显示在页面及页面和后台相互传值

    通过Ajax传到后台一个值,根据该值返回数据库表中的某一列的值,然后逐个显示到页面,并且给每个加上超链接,可以进行点击查看详细信息 1.通过Ajax传到后台一个值,红色部分为往Django后台传值,蓝 ...

  6. Linux平台下_tomcat的安装与优化

    一.Tomcat介绍 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选.对于一 ...

  7. CodeForces 527C. Glass Carving (SBT,线段树,set,最长连续0)

    原题地址:http://codeforces.com/problemset/problem/527/C Examples input H V V V output input H V V H V ou ...

  8. LeetCode No.94,95,96

    No.94 InorderTraversal 二叉树的中序遍历 题目 给定一个二叉树,返回它的中序 遍历. 示例 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶:递 ...

  9. day13-面向对象

    #解决同一类问题,使用面向对象的思想.类是制造对象的模具,类是抽象的,我们能知道它有哪些属性(name,age,saraly),但不知道具体的属性值. #看下面代码:类Penson制造了实例化对象re ...

  10. IPC之——消息队列

    消息队列作用: 可以用于两个没有联系的进程间通信,创建一个消息队列类似于打开了一个文件,两个不同的进程都可以进行操作 消息队列之函数介绍: 头文件:<sys/type.h> <sys ...