RabbitMQ Exchange详解以及Spring中Topic实战
前言
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦。
业务需求
后端(集群)通过websocket往各自维持的websocket session推送消息,如果采用每个实例监听同一个queue,那么生产者往该queue中推送一条消息,该消息只能被集群中某个实例消费一次。
想要实现后端每个实例同时消费该消息,便可采用RabbitMQ中的topic模式,即每个实例启动时,新建一个topic类型的exchange,routing key为"#.queue",并且每个实例的queue与该exchange绑定。每个实例中,根据hostname+".queue"(queue为配置文件中指定的默认队列名称)来创建各自的queue,这样每个实例都各自监听自己的queue。
生产者往该exchange中推送消息,routing key使用默认队列(或者每台实例自己的队列都可以,只要能匹配到"#.queue"即可),这样exchange接收到消息后,会根据routingkey来匹配与该exchange绑定的queues,并将消息发送到符合条件的queues中,这样每台实例都能收到该消息并消费。
RabbitMQ介绍
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
整体结构图

概念介绍
- Broker:简单来说就是消息队列服务器实体
- Virtual Host:虚拟主机,一个Broker里可以开设多个virtual host,用作不同用户的权限分离
- Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列
- Queue:消息队列载体,每个消息都会被投入到一个或多个队列
- Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来
- Routing Key:路由关键字,exchange根据这个关键字进行消息投递
- Publisher:消息生产者
- Consumer:消息消费者
- Channel:消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务
Exchange详解
Exchange意思为交换机,从图中可以看出,publisher发送消息,先进入Exchange,然后由Exchange分配到队列Queue。
1. Direct-Exchange
Direct Exchange是RabbitMQ Broker的默认Exchange,在此类型下,不必指定routing key的名字,创建的Queue有一个默认的routing key,一般与创建的Queue同名。
2. Topic-Exchange
Topic Exchange是根据routing key和Exchange的类型将message发送到一个或者多个Queue中,可以通过它来实现pub/sub模式,即发布订阅。
注:“#”表示0个或若干个关键字,“*”表示一个关键字
如果Exchange没有发现能够与RouteKey匹配的Queue,则会抛弃此消息
3. Fanout-Exchange
此类型的Exchange比较特殊,会忽略routing key的存在,直接将message广播到所有的Queue中。
4. Headers-Exchange
Headers Exchange不同于上面三种Exchange,它是根据Message的一些头部信息来分发过滤Message,忽略routing key的属性,如果Header信息和message消息的头信息相匹配,那么这条消息就匹配上了。
Spring集成RabbitMQ实现Topic模式
1. rabbitmq.properties(mq配置文件)
rabbitmq.host=127.0.0.1
rabbitmq.port=5672
rabbitmq.username=admin
rabbitmq.password=admin
rabbitmq.my.exchange=my-exchange
rabbitmq.my.queue=my-queue
2. 自动注入配置类(注意:需要创建自定义rabbitAdmin,否则启动时不会自动创建exchange、queue、bindings相关信息,具体可以查看RabbitAdmin类中的initialize()方法)
/**
* @Description: rabbit mq相关配置
* @author yehaixiao
* @date 2018年5月30日
*/
@Configuration
public class RabbitMQConfig { private static final Logger LOGGER = Logger.getLogger(RabbitMQConfig.class); @Value("${rabbitmq.host}")
String host;
@Value("${rabbitmq.port}")
int port;
@Value("${rabbitmq.username}")
String username;
@Value("${rabbitmq.password}")
String password;
@Value("${rabbitmq.my.exchange}")
String exchange;
@Value("${rabbitmq.my.queue}")
String queue; /**
* 创建rabbit mq连接工厂
*/
@Bean(name = "rabbitConnectionFactory")
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(host + ":" + port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
} /**
* 自定义connectionFactory,需要声明rabbitAdmin 内部initialize()方法会进行exchanges、queues、bindings声明
*/
@Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(connectionFactory());
} /**
* 创建amqp消息模版,用于发送者发送消息
*/
@Bean
public AmqpTemplate amqpTemplate() {
RabbitTemplate amqpTemplate = new RabbitTemplate(connectionFactory());
// json消息转化
amqpTemplate.setMessageConverter(messageConverter());
return amqpTemplate;
} /**
* 创建topic类型交换器
*/
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(exchange, false, true);
} /**
* 根据hostname创建queue(目的:为了实现多个集群中多个实例共享一条消息)
* durable:是否持久化
* exclusive:仅创建者可以使用的私有队列,断开后自动删除
* auto-delete:当所有消费端连接断开后,是否自动删除队列
*/
@Bean
public Queue queue() {
InetAddress netAddress = null;
try {
netAddress = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
String hostName = netAddress.getHostName();
String queueName = String.format("%s." + queue, hostName);
LOGGER.info("dynamic create queue name:" + queueName);
Queue queue = new Queue(queueName, false, false, true);
return queue;
} /**
* 将队列queue与exchange绑定,binding_key为#.queue,模糊匹配
*/
@Bean
public Binding binding(Queue queue, TopicExchange topicExchange) {
return BindingBuilder.bind(queue).to(topicExchange).with("#." + queue);
} /**
* 创建消息监听器
*/
@Bean
public MessageListener customizeMessageListener() {
return new CustomizeMessageListener();
} /**
* 创建json消息转化器
*/
@Bean
public MessageConverter messageConverter() {
Jackson2JsonMessageConverter fastJsonMessageConverter = new Jackson2JsonMessageConverter();
return fastJsonMessageConverter;
} /**
* 绑定queue和listener关系
*/
@Bean
public SimpleMessageListenerContainer mqMessageContainer(Queue queue, MessageListener customizeMessageListener) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
container.setQueues(queue);
container.setExposeListenerChannel(true);
container.setAcknowledgeMode(AcknowledgeMode.AUTO);
container.setMessageListener(customizeMessageListener);
return container;
}
}
3. 消息消费者(通过实现MessageListener来重写onMessage()方法)
/**
* @Description: 消息监听器
* @author yehaixiao
* @date 2018年5月24日
*/
@Service
public class CustomizeMessageListener implements MessageListener { private static final Logger LOGGER = LoggerFactory.getLogger(CustomizeMessageListener.class); @Override
public void onMessage(Message message) {
try {
String queue = message.getMessageProperties().getConsumerQueue();
String msg = new String(message.getBody());
LOGGER.info("consumer msg : {}, from queue : {}", msg, queue);
} catch (Exception e) {
LOGGER.error("consumer message error!", e);
}
}
}
4. 消息生产者(通过AmqpTemplate发送消息)
/**
* @Description: 消息生产者
* @author yehaixiao
* @date 2018年5月28日
*/
@Service
public class MessageProducer { private static final Logger LOGGER = LoggerFactory.getLogger(MessageProducer.class); @Value("${rabbitmq.my.exchange}")
private String exchange; @Value("${rabbitmq.my.queue}")
private String queue; @Resource
private AmqpTemplate amqpTemplate; public void sendMessage(Object message) {
LOGGER.info("send message : {} , to queue : {}", JSON.toJSONString(message), queue);
// message是需要传递的信息, 指定特定的queue
amqpTemplate.convertAndSend(exchange, queue, message);
}
}
RabbitMQ Exchange详解以及Spring中Topic实战的更多相关文章
- .NET 云原生架构师训练营(模块二 基础巩固 RabbitMQ Masstransit 详解)--学习笔记
2.6.7 RabbitMQ -- Masstransit 详解 Consumer 消费者 Producer 生产者 Request-Response 请求-响应 Consumer 消费者 在 Mas ...
- 由一个RABBITMQ监听器死循环引出的SPRING中BEAN和MAPPER接口的注入问题
1 @Slf4j 2 @RestController 3 @Component 4 public class VouchersReceiverController implements Message ...
- 详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别
详解 $_SERVER 函数中QUERY_STRING和REQUEST_URI区别 http://blog.sina.com.cn/s/blog_686999de0100jgda.html 实例: ...
- 详解 Go 语言中的 time.Duration 类型
swardsman详解 Go 语言中的 time.Duration 类型swardsman · 2018-03-17 23:10:54 · 5448 次点击 · 预计阅读时间 5 分钟 · 31分钟之 ...
- 详解jquery插件中(function ( $, window, document, undefined )的作用。
1.(function(window,undefined){})(window); Q:(function(window,undefined){})(window);中为什么要将window和unde ...
- zz详解深度学习中的Normalization,BN/LN/WN
详解深度学习中的Normalization,BN/LN/WN 讲得是相当之透彻清晰了 深度神经网络模型训练之难众所周知,其中一个重要的现象就是 Internal Covariate Shift. Ba ...
- [转载]详解网络传输中的三张表,MAC地址表、ARP缓存表以及路由表
[转载]详解网络传输中的三张表,MAC地址表.ARP缓存表以及路由表 虽然学过了计算机网络,但是这部分还是有点乱.正好在网上看到了一篇文章,讲的很透彻,转载过来康康. 本文出自 "邓奇的Bl ...
- 详解WebService开发中四个常见问题(2)
详解WebService开发中四个常见问题(2) WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...
- 详解WebService开发中四个常见问题(1)
详解WebService开发中四个常见问题(1) WebService开发中经常会碰到诸如WebService与方法重载.循环引用.数据被穿该等等问题.本文会给大家一些很好的解决方法. AD:WO ...
随机推荐
- 选择困难症的福音——团队Scrum冲刺阶段-Day 4
选择困难症的福音--团队Scrum冲刺阶段-Day 4 今日进展 编写提问部分 做了不同问题所对应的游戏选项,但关于游戏分类的界面还没有做完 增加功能 昨天在主界面增加"关于我们" ...
- 【转】nc 使用说明
netcat是网络工具中的瑞士军刀,它能通过TCP和UDP在网络中读写数据.通过与其他工具结合和重定向,你可以在脚本中以多种方式使用它.使用netcat命令所能完成的事情令人惊讶. netcat所做的 ...
- C++ MFC棋牌类小游戏day5
先整理一下之前的内容: 1.画了棋盘,把棋盘的每个点的状态都保存起来. 2.画棋子,分别用tiger类和people类画了棋子,并且保存了棋子的初始状态. 下面开始设计棋子的移动: 1.单机棋子,选中 ...
- GitHub上好的Java项目
1. java-design-patterns(Star:36k)Github地址:https://github.com/iluwatar/java-design-patterns 介绍:设计模式是形 ...
- 【对比分析四】position的absolute与fixed共同点与不同点
共同点: (1) 改变行内元素的呈现方式,display被置为block: (2) 让元素脱离普通流,不占据空间: (3) 默认会覆盖到非定位元素上 不同点: absolute的”根元素“是可以设置的 ...
- centos7系统下,配置学校客户端网络记录
存在的情况 1.学校的网络客户端绑定了个人的电脑MAC地址.绑定了IP地址. 2.我有两台笔记本,一台用了4年多,想用这台(B)直接装centos7系统,然后新买的笔记本(A)做为经常用的,系统为wi ...
- cant found Microsoft.VSSDK.BuildTools.15.0.26201
如果在vs扩展开发中出现 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 Failed to load 'C:\程序\EncodingNormalior\packages\Microsoft. ...
- SVN 客户端 TortoiseSVN 的安装和使用
关于 参考博客:TortoiseSVN新人使用指南 TortoiseSVN 是一个 Apache Subversion(SVN)客户端,实现为Windows外壳扩展.它直观且易于使用,因为它不需要Su ...
- AndroidStudio制作底部导航栏以及用Fragment实现切换功能
前言 大家好,给大家带来AndroidStudio制作底部导航栏以及用Fragment实现切换功能的概述,希望你们喜欢 学习目标 AndroidStudio制作底部导航栏以及用Fragment实现切换 ...
- Python 基础:分分钟入门
Python和Pythonic Python是一门计算机语言(这不是废话么),简单易学,上手容易,深入有一定困难.为了逼格,还是给你们堆一些名词吧:动态语言.解释型.网络爬虫.数据处理.机器学习.We ...