RabbitMQ入门(3)——发布/订阅(Publish/Subscribe)
在上一篇RabbitMQ入门(2)——工作队列中,有一个默认的前提:每个任务都只发送到一个工作人员。这一篇将介绍发送一个消息到多个消费者。这种模式称为发布/订阅(Publish/Subscribe)。
为了说明这种模式,我们将会构建一个简单的日志系统。它包含三个程序:生成日志消息的程序,将消息打印到控制台的程序和将消息保存到日志文件的程序。
交换机(Exchange)
前面介绍了队列如何接发消息。接下来介绍RabbitMQ的完整消息传递模型。首先快速回顾一下前一篇的内容:
- 生产者是发送消息的用户应用程序
- 队列是存储消息的缓存
- 消费者是接收消息的用户应用程序
RabbitMQ的消息传递模型核心思想是生产者从不将任何消息直接发送到队列。实际上,通常来说,生产者甚至不知道消息是否会被发送到任何队列。相反,生产者只能发送消息到交换机。一方面,交换机从生产者接收消息,另一方面,将消息推送到队列中。交换机必须知道如何处理接收到的消息。是否应该添加到具体队列里?还是添加到多个队列里?还是应该被放弃。这些规则是由交换机类型决定的。交换机类型:direct、topic、headers和fanout。

下面我们将首先介绍fanout类型。fanout,顾名思义,它的方式就是将接收到的所有消息都发送到它所知道的队列中。声明交换机:
channel.exchangeDeclare("test_logs", "fanout");
在前面的例子中,并没有提到交换机,但是依旧能将消息发送到队列。这是因为使用了一个默认的交换机,它的标识符是""。
前面发布消息的方式:
channel.basicPublish("", "hello", null, message.getBytes());
第一个参数就是交换机的名称。空字符串表示默认值或匿名交换机,如果routingKey存在(第二个参数),消息由routingKey决定发送到哪个队列。
临时队列(Temporary queue)
前面的例子中,队列都有一个指定的名称,因为它对于在生产者和消费者之间共享队列是至关重要的。然而,在本篇的日志系统中并不重要,我们需要了解的是所有的日志消息,而不是其中的一部分,同时,我们只对当前正在传递的消息感兴趣。为了满足这种需要,需要满足两点:
- 当连接到RabbitMQ时,需要一个新的,空的队列。可以随机生成一个队列名。或者更好地,让RabbitMQ为我们选择一个随机的队列名。
- 当消费者断开连接时,队列需要自动删除。
创建一个非持久化的、唯一的、自动删除的队列:
String queue = channel.queueDeclare().getQueue();
绑定(Binding)

现在,我们已经创建了一个fanout交换机和队列。通过绑定告诉交换机发送消息到队列。
channel.queueBind(queue, "test_logs", "");
代码清单
日志发送端:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeoutException;
public class EmitLog {
private static final String EXCHANGE_NAME = "test_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String message = Thread.currentThread().getName() + " -- " + format.format(new Date()) + " logging...";
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println(Thread.currentThread().getName() + " 发送消息:" + message);
channel.close();
connection.close();
}
}
从上面的例子可以看出,当创建一个连接后,我们声明了交换机,这一步是必须的,禁止向一个不存在的交换机发布消息。
如果没有队列绑定到交换机,消息将会丢失,这对我们来说是可行的。如果没有消费者监听,我们可以安全地丢弃这个消息。
打印日志到控制台:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ReceivLogsToConsole {
private static final String EXCHANGE_NAME = "test_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, EXCHANGE_NAME, "");
System.out.println("准备接收消息......");
final 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(Thread.currentThread().getName() + " 接收消息:" + message);
}
};
channel.basicConsume(queue, true, consumer);
}
}
存储日志到文件:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeoutException;
public class ReceiveLogsToSave {
private static final String EXCHANGE_NAME = "test_logs";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
String queue = channel.queueDeclare().getQueue();
channel.queueBind(queue, EXCHANGE_NAME, "");
System.out.println("准备接收消息......");
final 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");
print2File(message);
}
};
channel.basicConsume(queue, true, consumer);
}
private static void print2File(String message) throws FileNotFoundException {
try {
String dir = ReceiveLogsToSave.class.getClassLoader().getResource("").getPath();
String logFileName = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
File file = new File(dir, logFileName + ".txt");
FileOutputStream fos = new FileOutputStream(file, true);
fos.flush();
fos.write((message + "\r\n").getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
RabbitMQ入门(3)——发布/订阅(Publish/Subscribe)的更多相关文章
- RabbitMQ入门教程(五):扇形交换机发布/订阅(Publish/Subscribe)
原文:RabbitMQ入门教程(五):扇形交换机发布/订阅(Publish/Subscribe) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. ...
- RabbitMQ学习总结 第四篇:发布/订阅 Publish/Subscribe
目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...
- RabbitMQ入门:发布/订阅(Publish/Subscribe)
在前面的两篇博客中 RabbitMQ入门:Hello RabbitMQ 代码实例 RabbitMQ入门:工作队列(Work Queue) 遇到的实例都是一个消息只发送给一个消费者(工作者),他们的消息 ...
- RabbitMQ入门教程——发布/订阅
什么是发布订阅 发布订阅是一种设计模式定义了一对多的依赖关系,让多个订阅者对象同时监听某一个主题对象.这个主题对象在自身状态变化时,会通知所有的订阅者对象,使他们能够自动更新自己的状态. 为了描述这种 ...
- rabbitmq消息队列——"发布订阅"
三."发布订阅" 上一节的练习中我们创建了一个工作队列.队列中的每条消息都会被发送至一个工作进程.这节,我们将做些完全不同的事情--我们将发送单个消息发送至多个消费者.这种模式就是 ...
- RabbitMQ/JAVA (发布/订阅模式)
发布/订阅模式即生产者将消息发送给多个消费者. 下面介绍几个在发布/订阅模式中的关键概念-- 1. Exchanges (转发器) 可能原来我们都是基于一个队列发送和接收消息.现在介绍一下完整的消息传 ...
- (转) RabbitMQ学习之发布/订阅(java)
http://blog.csdn.net/zhu_tianwei/article/details/40887733 参考:http://blog.csdn.NET/lmj623565791/artic ...
- 4.RabbitMQ系列之发布/订阅模式
我们把一个消息转发给多个消费者,这种模式称之为发布-订阅模式 1.交换器(Exchange) RabbitMq消息模式的核心思想是:一个生产者并不会直接往一个队列中发送消息,事实上,生产者根本不知道它 ...
- RabbitMQ入门_12_发布方确认
参考资料:https://www.rabbitmq.com/confirms.html 通过 ack 机制,我们可以确保队列中的消息一定能被消费到.那我们有办法保证消息发布方一定把消息发送到队列了吗? ...
随机推荐
- OKEx量化分析报告[2017-12-09]
OKEx量化分析报告[2017-12-09] [分析时间]2017-12-09 15:24 [分析对象]OKEx [计价币种]USDT [报告内容]DASH_USDT短期 -3.8中期 ...
- 安全测试之bWAPP环境搭建
本篇文章介绍独立安装部署bwapp. 安装环境:window7+IIS7+mysql5.6+php5.6 bWAPP下载地址:https://sourceforge.net/projects/bwap ...
- CF#301 C:Ice Cave(简单BFS)
C:Ice Cave 有一个m*n的地图,里面包含'.'表示完整的冰块,'X'表示有裂痕的冰块,当游戏者到达完整的冰块时,这个位置的冰块会变成有裂痕的冰块,如果到达有裂痕的冰块时,游戏者会进入下一关 ...
- python自然语言处理——学习笔记:Chapter3纠错
2017-12-06更新:很多代码执行结果与书中不一致,是因为python的版本不一致.如果发现有问题,可以参考英文版: http://www.nltk.org/book/ 第三章,P87有一段处理h ...
- linux下的时间管理概述
2017/6/21 时间这一概念在生活中至关重要,而在操作系统中也同样重要,其在系统中的功能绝不仅仅是给用户提供时间这么简单,内核的许多机制都依赖于时间子系统.但凡是要在某个精确的时间执行某个事件,必 ...
- Python下的正则表达式原理和优化笔记
摘要: 本文旨在总结一些编写表达式的技巧和原理.鉴于介绍python中re模块的使用方法的文章太多.所以本文在基础方面都是略过,而在回溯原理和一些技巧方面记录一点点学习总结. 目录:[ - ] 基础规 ...
- 19.Eclipse 修改默认的keystore签名文件
Android开发中apk运行都需要签名,就算连接手机直接运行调试,apk都有签名,开发工具会有默认的debug_keyStore Eclipse ADT调试运行使用的是临时生成的Debug专用证书, ...
- MongoDB的安全校验
一.MongoDB安全校验的重要性 每个MongoDB实例中的数据库都可以有许多用户.如果没有开启安全校验,限制用户权限,则每个进到数据库的用户都能任意的对数据库数据进行读,写甚至是读写操作.这样的场 ...
- es6函数模块-------初步学习
初步学习: 函数参数允许尾逗号 function clownsEverywhere( param1, param2, //param2后面有逗号 ) { } 函数参数可以赋初值 利用解构赋值默认值结合 ...
- 如何注册iClap账号?
iClap是一个以产品为基础,集成员工日常所需工具,打通项目工作流程,实现员工自我工作管理.项目管理和企业管理三个体系协同的管理规范系统. 工具/原料 iPhone/Android/浏览器 Teamb ...