【RabbitMQ】Publish/Subscribe
Publish/Subscribe
在上一节我们创建了一个work queue。背后的设想为每个任务被分发给明确的消费者。这节内容我们将做一些完全不同的事情 -- 我们将发送一条消息给多个消费者。这种模式被称为“发布/订阅”。
为了描述这种模式,我们来构建一个简单的日志系统。它包含两个程序 -- 第一个将会发送日志消息,第二个接收并打印。在我们的日志系统中,所有的正在运行的接收程序都会收到消息。这样我们可以运行一个接收程序,将日志定向到磁盘;同时可以运行另外的接收程序可以从屏幕上看到日志。
本质上,发布的所有日志消息会被广播给所有的接受者。
交换机
在前几节内容中,我们都是从一个队列中发送和接收消息。现在是时候介绍RabbitMQ的完整消息模型了。
快速回顾前面章节:
- 一个生产者是一个发送消息的用户应用
- 一个队列是一个存放消息的缓冲区
- 一个消费者是一个接收消息的用户应用
RabbitMQ消息模型的核心思想是,生产者从来不会直接发送消息给一个队列。确切的说,大多数情况下,生产者根本不知道它的消息将会发送到哪个队列。
事实是,生产者只能发送消息给一个交换机(exchange)。交换机是一个很简单的概念。一方面它接收生产者的消息,另一方面它推送消息到队列中。但是交换机必须明确知道自己要对接收到的消息进行何种处理:是添加到制定队列?还是添加到所有的队列?抑或是将之丢弃?这些规则由交换机的类型来定义。

有许多可供选择的交换机类型:direct, topic, headers, fanout. 我们集中在fanout上讲解。创建一个fanout类型的交换机,称它为logs:
channel.exchangeDeclare("logs", "fanout");
这个交换机非常简单。它会广播所有接收到的消息给所有它的已知队列。这就是我们logger程序所需要的。
Listing exchanges
可以使用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.
这个列表中有一些amp.*的交换机和默认(未命名)的交换机。它们都是默认被创建的。
Nameless exchange
之前的章节中我们对交换机一无所知,但却仍然可以发送消息到队列中。这很可能是因为我们使用了默认的交换机,我们用空字符串("")标识了它。
回想我们如何发布一条消息的:
channel.basicPublish("", "hello", null, message.getBytes());
第一个参数就是交换机的名字。空字符串表示默认或未命名的交换机:消息根据路由Key指定的队列名称被路由到队列。
现在我们可以发布到我们自己命名的交换机:
channel.basicPublish( "logs", "", null, message.getBytes());
临时队列
你可能还记得我们之前使用的队列都是有一个指定的名称的(比如hello和task_queue)。给队列命名对我们来说至关重要 -- 我们需要将消费者指向相同的队列。当你想在生产者和消费者之间分享队列的时候,给队列一个名字非常重要。
但我们的日志程序不是这样的。我们希望监听所有的日志消息,而不仅仅是其中的一部分。我们也仅仅对当前的流动消息感兴趣,而不是老的消息。解决这个问题需要下面两件事:
第一,无论何时我们连接到RabbitMQ时,都需要一个新的空的队列。要做到这一点我们可以创建一个随机名称的队列,或者更好一点的方式 - 让服务器为我们选择一个随机队列名。
第二,一旦我们断开生产者的连接,队列应该被自动删除。
在Java客户端,当我们调用无参的queueDeclare()方法,我们将创建一个非持久化的,唯一的,自动删除的并且随机名称的队列。
String queueName = channel.queueDeclare().getQueue();
queueName是一个随机的队列名称,可能看起来像:amq.gen-JzTY20BRgKO-HjmUJj0wLg
绑定

现在我们已经创建了一个fanout交换机和一个队列。现在我们需要告诉交换机给我们的队列发送消息。这种在交换机和队列之间的关系叫做绑定(binding)。
channel.queueBind(queueName, "logs", "");
现在开始,logs交换机将向我们的队列追加消息。
Listing bingdings
使用rabbitmqctl list_bindings列出所有存在的绑定
Putting it all together

生产者程序,发送日志消息,看起来和之前的程序没有什么太大的区别。最重要的改变在我们现在希望发布消息到logs交换机,而不是之前的没有名字的交换机。在发送的时候,我们需要提供一个路由Key(routingKey),但是它的值被fanout交换机忽略了。下面是EmitLog.java:
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Channel; public class EmitLog { private static final String EXCHANGE_NAME = "logs"; public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'"); channel.close();
connection.close();
} private static String getMessage(String[] strings){
if (strings.length < 1)
return "info: Hello World!";
return joinStrings(strings, " ");
} private static String joinStrings(String[] strings, String delimiter) {
int length = strings.length;
if (length == 0) return "";
StringBuilder words = new StringBuilder(strings[0]);
for (int i = 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}
如你所见,在创建了连接之后,我们声明了交换机。这一步是必须的,因为无法向一个不存在的交换机发布消息。
如果没有队列绑定到交换机上,消息会丢失,但对我们来说这没有什么,如果没有消费者监听我们可以安全的丢弃消息。
ReceiveLogs.java:
import com.rabbitmq.client.*;
import java.io.IOException;
public class ReceiveLogs {
private static final String EXCHANGE_NAME = "logs";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, "");
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
【RabbitMQ】Publish/Subscribe的更多相关文章
- 【RabbitMQ】显示耗时处理进度
[RabbitMQ]显示耗时处理进度 通过网页提交一个耗时的请求,然后启动处理线程,请求返回.处理线程每完成一部分就给前台推送完成的数量,前端显示进度. 依赖jar <?xml version= ...
- 【RabbitMQ】 WorkQueues
消息分发 在[RabbitMQ] HelloWorld中我们写了发送/接收消息的程序.这次我们将创建一个Work Queue用来在多个消费者之间分配耗时任务. Work Queues(又称为:Task ...
- 【rabbitmq】rabbitmq集群环境搭建
安装rabbitmq-server 总共有3台虚拟机,都安装有rabbitmq服务,安装过程可参考: [rabbitmq]Centos7 下安装rabbitmq 创建用户和vhost 说明: 此步骤不 ...
- 【RabbitMQ】 Java简单的实现RabbitMQ
准备工作 1.安装RabbitMQ,参考[RabbitMQ] RabbitMQ安装 2.新建Java项目,引入RabbitMQ的Maven依赖 <dependency> <group ...
- 【RabbitMQ】 RabbitMQ配置开机启动
环境 系统:Linux(CentOS 7.2) Erlang环境:21.1(安装参考[Erlang]源码安装) RabbitMQ:3.7.9(安装参考[RabbitMQ] RabbitMQ安装) 配置 ...
- 【RabbitMQ】使用学习
[RabbitMQ]使用学习 转载: ============================================================= =================== ...
- 【RabbitMQ】 RabbitMQ安装
MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的是程序之间 ...
- 【rabbitmq】rabbitmq概念解析--消息确认--示例程序
概述 本示例程序全部来自rabbitmq官方示例程序,rabbitmq-demo: 官方共有6个demo,针对不同的语言(如 C#,Java,Spring-AMQP等),都有不同的示例程序: 本示例程 ...
- 【RabbitMQ】1、RabbitMQ的几种典型使用场景
RabbitMQ主页:https://www.rabbitmq.com/ AMQP AMQP协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现.它主要包括以下组件: 1.Serve ...
随机推荐
- C++中使用初始化列表的情况
http://blog.csdn.net/iceshirley/article/details/5688696 要理解这个问题,从概念上,我们要知道一点,那就是构造函数的执行过程会分成两个阶段:隐式或 ...
- Alcatraz,
今天重装Alcatraz,运行github上的命令后,安装成功,可是配置界面没出来. 无奈重装, 先删除原本的插件 rm -rf ~/Library/Application\ Support/Deve ...
- 【Network】UDP 大包怎么发? MTU怎么设置?
这里主要用UDP来发送视频,当发送的数据大于1500时分包发送,保证每包小于1500.发送好办,分割后循环发就可以了,关键是接收时的处理.先做一下处理的方法 :发送时每包上面加上标识,比如RTP的做法 ...
- Linux压缩和解压汇总
各种后缀的压缩包压缩方法和解压方法 压缩包 压缩 解压 .tar.gz和.tgz tar -czf jpg.tar.gz *.jpg tar -xzvf filename -C path .tar.b ...
- .NET LINQ查询语法与方法语法
LINQ 查询语法与方法语法 通过使用 C# 3.0 中引入的声明性查询语法,介绍性 LINQ 文档中的多数查询都被编写为查询表达式. 但是,.NET 公共语言运行时 (CLR) 本身并不具 ...
- 【XLL 框架库函数】 QuitFramework
去初使化框架库,简问题是才的重新初使化 XLOPER/XLOPER12. 参数 这个函数没有参数 属性值/返回值 这个函数没有返回值.
- python--基础学习(五)参数位置传递、关键字传递、包裹传递及解包裹
python系列均基于python3.4环境 1.位置传递和关键字传递 代码示例 #位置传递 def fun(a,b,c): print("a: {0}, b: {1}, c: {2}&qu ...
- scala安装
一:在官网下载相应的版本http://www.scala-lang.org/download/2.10.6.html 二,在linux中解压下载下来的scala包 三:配置环境变量 export ...
- 【转载】CentOS服务器配置VPN详解
转载来自: https://bbs.aliyun.com/read/162297.html http://www.wanghailin.cn/centos-7-vpn/ 操作系统:CentOS 6.3 ...
- Datatables JQuery插件
Datatables是一款jquery表格插件.它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能. 分页,即时搜索和排序 几乎支持任何数据源:DOM, javascript, Ajax ...