SpringBoot连接多RabbitMQ源

在实际开发中,很多场景需要异步处理,这时就需要用到RabbitMQ,而且随着场景的增多程序可能需要连接多个RabbitMQ。SpringBoot本身提供了默认的配置可以快速配置连接RabbitMQ,但是只能连接一个RabbitMQ,当需要连接多个RabbitMQ时,默认的配置就不太适用了,需要单独编写每个连接。

在SpringBoot框架中,我们常用的两个类一般是:

  • RabbitTemplate:作为生产、消费消息使用;
  • RabbitAdmin:作为申明、删除交换机和队列,绑定和解绑队列和交换机的绑定关系使用。

所以我们连接多个RabbitMQ就需要重新建立连接、重新实现这两个类。

代码如下:

配置

application.properties配置文件需要配置两个连接:

server.port=8080

# rabbitmq
v2.spring.rabbitmq.host=host
v2.spring.rabbitmq.port=5672
v2.spring.rabbitmq.username=username
v2.spring.rabbitmq.password=password
v2.spring.rabbitmq.virtual-host=virtual-host
#consume 手动 ack
v2.spring.rabbitmq.listener.simple.acknowledge-mode=manual
#1.当mandatory标志位设置为true时,
# 如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
# 那么broker会调用basic.return方法将消息返还给生产者;
#2.当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;通俗的讲,
# mandatory标志告诉broker代理服务器至少将消息route到一个队列中,
# 否则就将消息return给发送者;
v2.spring.rabbitmq.template.mandatory=true
#publisher confirms 发送确认
v2.spring.rabbitmq.publisher-confirms=true
#returns callback :
# 1.未送达exchange
# 2.送达exchange却未送道queue的消息 回调returnCallback.(注意)出现2情况时,publisher-confirms 回调的是true
v2.spring.rabbitmq.publisher-returns=true
v2.spring.rabbitmq.listener.simple.prefetch=5 # rabbitmq
v1.spring.rabbitmq.host=host
v1.spring.rabbitmq.port=5672
v1.spring.rabbitmq.username=username
v1.spring.rabbitmq.password=password
v1.spring.rabbitmq.virtual-host=virtual-host
#consume 手动 ack
v1.spring.rabbitmq.listener.simple.acknowledge-mode=manual
#1.当mandatory标志位设置为true时,
# 如果exchange根据自身类型和消息routingKey无法找到一个合适的queue存储消息,
# 那么broker会调用basic.return方法将消息返还给生产者;
#2.当mandatory设置为false时,出现上述情况broker会直接将消息丢弃;通俗的讲,
# mandatory标志告诉broker代理服务器至少将消息route到一个队列中,
# 否则就将消息return给发送者;
v1.spring.rabbitmq.template.mandatory=true
#publisher confirms 发送确认
v1.spring.rabbitmq.publisher-confirms=true
#returns callback :
# 1.未送达exchange
# 2.送达exchange却未送道queue的消息 回调returnCallback.(注意)出现2情况时,publisher-confirms 回调的是true
v1.spring.rabbitmq.publisher-returns=true
v1.spring.rabbitmq.listener.simple.prefetch=5

重写连接工厂

需要注意的是,在多源的情况下,需要在某个连接加上@Primary注解,表示主连接,默认使用这个连接

package com.example.config.rabbitmq;

import com.alibaba.fastjson.JSON;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; /**
* Created by shuai on 2019/4/23.
*/
@Configuration
public class MultipleRabbitMQConfig { @Bean(name = "v2ConnectionFactory")
public CachingConnectionFactory hospSyncConnectionFactory(
@Value("${v2.spring.rabbitmq.host}") String host,
@Value("${v2.spring.rabbitmq.port}") int port,
@Value("${v2.spring.rabbitmq.username}") String username,
@Value("${v2.spring.rabbitmq.password}") String password,
@Value("${v2.spring.rabbitmq.virtual-host}") String virtualHost,
@Value("${v2.spring.rabbitmq.publisher-confirms}") Boolean publisherConfirms,
@Value("${v2.spring.rabbitmq.publisher-returns}") Boolean publisherReturns) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(publisherConfirms);
connectionFactory.setPublisherReturns(publisherReturns);
return connectionFactory;
} @Bean(name = "v2RabbitTemplate")
public RabbitTemplate firstRabbitTemplate(
@Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory,
@Value("${v2.spring.rabbitmq.template.mandatory}") Boolean mandatory) {
RabbitTemplate v2RabbitTemplate = new RabbitTemplate(connectionFactory);
v2RabbitTemplate.setMandatory(mandatory);
v2RabbitTemplate.setConfirmCallback((correlationData, ack, s) -> {
if (!ack) {
// LOGGER.info("{} 发送RabbitMQ消息 ack确认 失败: [{}]", this.name, JSON.toJSONString(object));
} else {
// LOGGER.info("{} 发送RabbitMQ消息 ack确认 成功: [{}]", this.name, JSON.toJSONString(object));
}
});
v2RabbitTemplate.setReturnCallback((message, code, s, exchange, routingKey) -> {
// LOGGER.error("{} 发送RabbitMQ消息returnedMessage,出现异常,Exchange不存在或发送至Exchange却没有发送到Queue中,message:[{}], code[{}], s[{}], exchange[{}], routingKey[{}]", new Object[]{this.name, JSON.toJSONString(message), JSON.toJSONString(code), JSON.toJSONString(s), JSON.toJSONString(exchange), JSON.toJSONString(routingKey)});
});
return v2RabbitTemplate;
} @Bean(name = "v2ContainerFactory")
public SimpleRabbitListenerContainerFactory hospSyncFactory(
@Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory,
@Value("${v2.spring.rabbitmq.listener.simple.acknowledge-mode}") String acknowledge,
@Value("${v2.spring.rabbitmq.listener.simple.prefetch}") Integer prefetch
) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.valueOf(acknowledge.toUpperCase()));
factory.setPrefetchCount(prefetch);
return factory;
} @Bean(name = "v2RabbitAdmin")
public RabbitAdmin iqianzhanRabbitAdmin(
@Qualifier("v2ConnectionFactory") ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
} // mq主连接
@Bean(name = "v1ConnectionFactory")
@Primary
public CachingConnectionFactory publicConnectionFactory(
@Value("${v1.spring.rabbitmq.host}") String host,
@Value("${v1.spring.rabbitmq.port}") int port,
@Value("${v1.spring.rabbitmq.username}") String username,
@Value("${v1.spring.rabbitmq.password}") String password,
@Value("${v1.spring.rabbitmq.virtual-host}") String virtualHost,
@Value("${v1.spring.rabbitmq.publisher-confirms}") Boolean publisherConfirms,
@Value("${v1.spring.rabbitmq.publisher-returns}") Boolean publisherReturns) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(publisherConfirms);
connectionFactory.setPublisherReturns(publisherReturns);
return connectionFactory;
} @Bean(name = "v1RabbitTemplate")
@Primary
public RabbitTemplate publicRabbitTemplate(
@Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory,
@Value("${v1.spring.rabbitmq.template.mandatory}") Boolean mandatory) {
RabbitTemplate v1RabbitTemplate = new RabbitTemplate(connectionFactory);
v1RabbitTemplate.setMandatory(mandatory);
v1RabbitTemplate.setConfirmCallback((correlationData, ack, s) -> {
if (!ack) {
// LOGGER.info("{} 发送RabbitMQ消息 ack确认 失败: [{}]", this.name, JSON.toJSONString(object));
} else {
// LOGGER.info("{} 发送RabbitMQ消息 ack确认 成功: [{}]", this.name, JSON.toJSONString(object));
}
});
v1RabbitTemplate.setReturnCallback((message, code, s, exchange, routingKey) -> {
// LOGGER.error("{} 发送RabbitMQ消息returnedMessage,出现异常,Exchange不存在或发送至Exchange却没有发送到Queue中,message:[{}], code[{}], s[{}], exchange[{}], routingKey[{}]", new Object[]{this.name, JSON.toJSONString(message), JSON.toJSONString(code), JSON.toJSONString(s), JSON.toJSONString(exchange), JSON.toJSONString(routingKey)});
});
return v1RabbitTemplate;
} @Bean(name = "v1ContainerFactory")
@Primary
public SimpleRabbitListenerContainerFactory insMessageListenerContainer(
@Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory,
@Value("${v1.spring.rabbitmq.listener.simple.acknowledge-mode}") String acknowledge,
@Value("${v1.spring.rabbitmq.listener.simple.prefetch}") Integer prefetch) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.valueOf(acknowledge.toUpperCase()));
factory.setPrefetchCount(prefetch);
return factory;
} @Bean(name = "v1RabbitAdmin")
@Primary
public RabbitAdmin publicRabbitAdmin(
@Qualifier("v1ConnectionFactory") ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}

创建Exchange、Queue并绑定

再实现RabbitAdmin后,我们就需要根据RabbitAdmin创建对应的交换机和队列,并建立绑定关系

package com.example.config.rabbitmq;

import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct;
import javax.annotation.Resource; /**
* 创建Queue、Exchange并建立绑定关系
* Created by shuai on 2019/5/16.
*/
@Configuration
public class MyRabbitMQCreateConfig { @Resource(name = "v2RabbitAdmin")
private RabbitAdmin v2RabbitAdmin; @Resource(name = "v1RabbitAdmin")
private RabbitAdmin v1RabbitAdmin; @PostConstruct
public void RabbitInit() {
v2RabbitAdmin.declareExchange(new TopicExchange("exchange.topic.example.new", true, false));
v2RabbitAdmin.declareQueue(new Queue("queue.example.topic.new", true));
v2RabbitAdmin.declareBinding(
BindingBuilder
.bind(new Queue("queue.example.topic.new", true)) //直接创建队列
.to(new TopicExchange("exchange.topic.example.new", true, false)) //直接创建交换机 建立关联关系
.with("routing.key.example.new")); //指定路由Key
}
}

生产者

为了后续验证每个连接都建立成功,并且都能生产消息,生产者这里分别使用新生成的RabbitTemplate发送一条消息。

package com.example.topic;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import javax.annotation.Resource; @Component
public class TopicProducer { @Resource(name = "v1RabbitTemplate")
private RabbitTemplate v1RabbitTemplate; @Resource(name = "v2RabbitTemplate")
private RabbitTemplate v2RabbitTemplate; public void sendMessageByTopic() {
String content1 = "This is a topic type of the RabbitMQ message example from v1RabbitTemplate";
v1RabbitTemplate.convertAndSend(
"exchange.topic.example.new",
"routing.key.example.new",
content1); String content2 = "This is a topic type of the RabbitMQ message example from v2RabbitTemplate";
v2RabbitTemplate.convertAndSend(
"exchange.topic.example.new",
"routing.key.example.new",
content2);
}
}

消费者

这里需要注意在配置消费队列时,需要标识ContainerFactory

package com.example.topic;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component; @Component
@RabbitListener(queues = "queue.example.topic.new", containerFactory = "v2ContainerFactory")
public class TopicConsumer { @RabbitHandler
public void consumer(String message) {
System.out.println(message);
}
}

这样就完成了SpringBoot连接多个RabbitMQ源的示例了,再写一段测试代码验证下。

测试验证

package com.example.test;

import com.example.topic.TopicProducer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class)
@SpringBootTest
public class RabbitMQMultipleTest { @Autowired
private TopicProducer topicProducer; @Test
public void topicProducerTest() {
topicProducer.sendMessageByTopic();
}
}

执行测试代码,验证结果为:

验证SpringBoot连接多RabbitMQ源成功!

关注公众号:java之旅

springboot入门系列(五):SpringBoot连接多RabbitMQ源的更多相关文章

  1. SpringBoot入门(五)——自定义配置

    本文来自网易云社区 大部分比萨店也提供某种形式的自动配置.你可以点荤比萨.素比萨.香辣意大利比萨,或者是自动配置比萨中的极品--至尊比萨.在下单时,你并没有指定具体的辅料,你所点的比萨种类决定了所用的 ...

  2. SpringBoot入门系列(转)

    SpringBoot入门系列:第一篇 Hello World http://blog.csdn.net/lxhjh/article/details/51711148

  3. SpringBoot入门系列(十一)统一异常处理的实现

    前面介绍了Spring Boot 如何整合定时任务已经Spring Boot 如何创建异步任务和定时任务.不清楚的朋友可以看看之前的文章:<Spring Boot 入门系列文章> 接下来主 ...

  4. SpringBoot入门系列(十二)统一日志收集

    前面介绍了Spring Boot 异常处理,不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/category/1657780.html. 今 ...

  5. C语言高速入门系列(五)

    C语言高速入门系列(五) C语言指针初涉                                           ------转载请注明出处:coder-pig 本节引言: 上一节我们对C ...

  6. vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...

  7. SpringBoot入门系列~Spring-Data-JPA自动建表

    1.pom.xml引入Spring-Data-Jpa和mysql依赖 <!-- Spring-data-jpa依赖 --> <dependency> <groupId&g ...

  8. springboot入门系列(二):SpringBoot整合Swagger

    上一篇<简单搭建SpringBoot项目>讲了简单的搭建SpringBoot 项目,而 SpringBoot 和 Swagger-ui 搭配在持续交付的前后端开发中意义重大,Swagger ...

  9. Go语言入门系列(五)之指针和结构体的使用

    Go语言入门系列前面的文章: Go语言入门系列(二)之基础语法总结 Go语言入门系列(三)之数组和切片 Go语言入门系列(四)之map的使用 1. 指针 如果你使用过C或C++,那你肯定对指针这个概念 ...

随机推荐

  1. 常用的Websocket技术一览

    1. 前言 Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议.WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据 ...

  2. python中random库的使用

    基本随机函数 计算机产生随机数是需要随机数种子的,例如 给定一个随机数种子,就能利用梅森旋转算法产生一系列随机序列 每一个数都是随机数,只要随机种子相同,产生的随机数和数之间的关系都是确定的 随机种子 ...

  3. k8s Docker 安装

    k8s Docker 安装 一.运行环境 Centos 7.7 虚拟机内核为 3.10 基础组件版本: k8s.gcr.io/kube-apiserver:v1.16.0 k8s.gcr.io/kub ...

  4. PowerJob 从 0 到 1.9k star 的经历

    本文适合有 Java 基础知识的人群 作者:HelloGitHub-Salieri 本文就是<讲解 PowerJob>系列的最后一篇文章了,纯粹是写点经历写点心路历程啥的,和大家道个别. ...

  5. kali学习wiki

    https://github.com/Keybird0/Kali-learning-notes/wiki

  6. Appium 用途和特点

    Appium 是一个移动 App (手机应用)自动化工具. 手机APP 自动化有什么用? 自动化完成一些重复性的任务 比如微信客服机器人 爬虫 就是通过手机自动化爬取信息. 为什么不通过网页.HTTP ...

  7. Python-py2和py3读写文本区别

    python2和python3的区别? python 2  str             对应      python3 bytes python 2 uincode            对应   ...

  8. 对抗生成网络 Generative Adversarial Networks

    1. Basic idea 基本任务:要得到一个generator,能够模拟想要的数据分布.(一个低维向量到一个高维向量的映射) discriminator就像是一个score function. 如 ...

  9. ACMer不得不会的线段树,究竟是种怎样的数据结构?

    大家好,欢迎阅读周三算法数据结构专题,今天我们来聊聊一个新的数据结构,叫做线段树. 线段树这个数据结构很多人可能会有点蒙,觉得没有听说过,但是它非常非常有名,尤其是在竞赛圈,可以说是竞赛圈的必备技能. ...

  10. python数据结构之图深度优先和广度优先实例详解

    本文实例讲述了python数据结构之图深度优先和广度优先用法.分享给大家供大家参考.具体如下: 首先有一个概念:回溯 回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到 ...