说明
需求场景:
项目中在已接入rabbitmq一个vhost的基础上,需要再引入多个vhost进行消息处理,spring原来的支持以及满足不了,所以自己要重写。
代码
自定义rabbitmq的pom信息:
spring:
rabbitmq:
one:
host: xxxx
port: 5672
virtual-host: one
username: xxxxx
password: xxxx
queue: xxx.xxx.xxx
topic:
exchange:
name: xxx_xxx_xxx
two:
host: xxxx
port: 5672
virtual-host: two
username: xxxx
password: xxxxx
queue: xxx.xxx.xxx
topic:
exchange:
name: xxx_xxx_xxx
重写one的连接工厂
在连接多个 MQ 的情况下,需要在某个连接加上 @Primary 注解,表示主连接,默认使用这个连接,如果不加,服务会起不来
import lombok.extern.slf4j.Slf4j;
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.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;
@Slf4j
@Configuration
/**
* 重写连接工厂
*/
public class OneMQConfig {
@Value("${spring.rabbitmq.one.host}")
private String host;
@Value("${spring.rabbitmq.one.port}")
private int port;
@Value("${spring.rabbitmq.one.username}")
private String username;
@Value("${spring.rabbitmq.one.password}")
private String password;
@Value("${spring.rabbitmq.one.virtual-host}")
private String virtualHost;
/**
* 定义与one的连接工厂
*/
@Bean(name = "oneConnectionFactory")
@Primary
public ConnectionFactory oneConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
return connectionFactory;
}
@Bean(name = "oneRabbitTemplate")
@Primary
public RabbitTemplate oneRabbitTemplate(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate oneRabbitTemplate = new RabbitTemplate(connectionFactory);
oneRabbitTemplate.setMandatory(true);
oneRabbitTemplate.setConnectionFactory(connectionFactory);
oneRabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* 确认消息送到交换机(Exchange)回调
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info("确认消息送到交换机(Exchange)结果:");
log.info("相关数据:{}", correlationData);
boolean ret = false;
if (ack) {
log.info("消息发送到交换机成功, 消息 = {}", correlationData.getId());
//下面可自定义业务逻辑处理,如入库保存信息等
} else {
log.error("消息发送到交换机失败! 消息: {}}; 错误原因:cause: {}", correlationData.getId(), cause);
//下面可自定义业务逻辑处理,如入库保存信息等
}
}
});
oneRabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
* 只要消息没有投递给指定的队列 就触发这个失败回调
* @param message 投递失败的消息详细信息
* @param replyCode 回复的状态码
* @param replyText 回复的文本内容
* @param exchange 当时这个消息发给那个交换机
* @param routingKey 当时这个消息用那个路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
//获取消息id
String messageId = message.getMessageProperties().getMessageId();
// 内容
String result = null;
try {
result = new String(message.getBody(), "UTF-8");
} catch (Exception e) {
log.error("消息发送失败{}", e);
}
log.error("消息发送失败, 消息ID = {}; 消息内容 = {}", messageId, result);
//下面可自定义业务逻辑处理,如入库保存信息等
}
});
return oneRabbitTemplate;
}
@Bean(name = "oneFactory")
@Primary
public SimpleRabbitListenerContainerFactory oneFactory(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory,
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
configurer.configure(factory, connectionFactory);
return factory;
}
@Bean(name = "oneRabbitAdmin")
@Primary
public RabbitAdmin oneRabbitAdmin(@Qualifier("oneConnectionFactory") ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}
重写two的连接工厂:
import lombok.extern.slf4j.Slf4j;
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.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;
@Slf4j
@Configuration
public class TwoMQConfig {
@Value("${spring.rabbitmq.two.host}")
private String host;
@Value("${spring.rabbitmq.two.port}")
private int port;
@Value("${spring.rabbitmq.two.username}")
private String username;
@Value("${spring.rabbitmq.two.password}")
private String password;
@Value("${spring.rabbitmq.two.virtual-host}")
private String virtualHost;
/**
* 定义与two的连接工厂
*/
@Bean(name = "twoConnectionFactory")
public ConnectionFactory twoConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(true);
connectionFactory.setPublisherReturns(true);
return connectionFactory;
}
@Bean(name = "twoRabbitTemplate")
public RabbitTemplate twoRabbitTemplate(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate twoRabbitTemplate = new RabbitTemplate(connectionFactory);
twoRabbitTemplate.setMandatory(true);
twoRabbitTemplate.setConnectionFactory(connectionFactory);
twoRabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* 确认消息送到交换机(Exchange)回调
* @param correlationData
* @param ack
* @param cause
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info("确认消息送到交换机(Exchange)结果:");
log.info("相关数据:{}", correlationData);
boolean ret = false;
if (ack) {
log.info("消息发送到交换机成功, 消息 = {}", correlationData.getId());
//下面可自定义业务逻辑处理,如入库保存信息等
} else {
log.error("消息发送到交换机失败! 消息: {}}; 错误原因:cause: {}", correlationData.getId(), cause);
//下面可自定义业务逻辑处理,如入库保存信息等
}
}
});
twoRabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
* 只要消息没有投递给指定的队列 就触发这个失败回调
* @param message 投递失败的消息详细信息
* @param replyCode 回复的状态码
* @param replyText 回复的文本内容
* @param exchange 当时这个消息发给那个交换机
* @param routingKey 当时这个消息用那个路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
//获取消息id
String messageId = message.getMessageProperties().getMessageId();
// 内容
String result = null;
try {
result = new String(message.getBody(), "UTF-8");
} catch (Exception e) {
log.error("消息发送失败{}", e);
}
log.error("消息发送失败, 消息ID = {}; 消息内容 = {}", messageId, result);
//下面可自定义业务逻辑处理,如入库保存信息等
}
});
return twoRabbitTemplate;
}
@Bean(name = "twoFactory")
public SimpleRabbitListenerContainerFactory twoFactory(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory,
SimpleRabbitListenerContainerFactoryConfigurer configurer) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
configurer.configure(factory, connectionFactory);
return factory;
}
@Bean(name = "twoRabbitAdmin")
public RabbitAdmin twoRabbitAdmin(@Qualifier("twoConnectionFactory") ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
rabbitAdmin.setAutoStartup(true);
return rabbitAdmin;
}
}
创建队列、交换机并绑定:
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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
/**
* 创建队列、交换机并绑定
*/
@Configuration
public class QueueConfig {
@Resource(name = "oneRabbitAdmin")
private RabbitAdmin oneRabbitAdmin;
@Resource(name = "twoRabbitAdmin")
private RabbitAdmin twoRabbitAdmin;
@Value("${spring.rabbitmq.one.queue}")
private String oneOutQueue;
@Value("${spring.rabbitmq.one.queue}")
private String oneRoutingKey;
@Value("${spring.rabbitmq.two.queue}")
private String twoOutQueue;
@Value("${spring.rabbitmq.two.queue}")
private String twoRoutingKey;
@Value("${spring.rabbitmq.one.topic.exchange.name}")
private String oneTopicExchange;
@Value("${spring.rabbitmq.two.topic.exchange.name}")
private String twoTopicExchange;
@PostConstruct
public void oneRabbitInit() {
//声明交换机
oneRabbitAdmin.declareExchange(new TopicExchange(oneTopicExchange, true, false));
//声明队列
oneRabbitAdmin.declareQueue(new Queue(oneOutQueue, true, false, false));
//绑定队列及交换机
oneRabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(oneOutQueue, true, false, false))
.to(new TopicExchange(oneTopicExchange, true, false))
.with(oneRoutingKey + ".#"));
}
@PostConstruct
public void twoRabbitInit() {
//声明交换机
twoRabbitAdmin.declareExchange(new TopicExchange(twoTopicExchange, true, false));
//声明队列
twoRabbitAdmin.declareQueue(new Queue(twoOutQueue, true));
//绑定队列及交换机
twoRabbitAdmin.declareBinding(BindingBuilder.bind(new Queue(twoOutQueue, true, false, false))
.to(new TopicExchange(twoTopicExchange, true, false))
.with(twoRoutingKey));
}
}
消费者消费消息
不同vhost换containerFactory里面的工厂和queue
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import com.ziyun.iot.event.model.mysql.event.entity.UserEvent;
import com.ziyun.iot.event.model.mysql.event.mapper.EventMapper;
import com.ziyun.iot.event.model.mysql.event.mapper.UserEventMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AddUserConsumer {
@RabbitListener(queues = "${spring.rabbitmq.two.queue}", containerFactory = "twoFactory")
public void addUserConsumer(Message message, Channel channel) {
String body = new String(message.getBody(), StandardCharsets.UTF_8);
log.info("收到rabbitmq消息: {}", body);
try {
//业务逻辑
//手动确认消息已经被消费
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("新增用户:{} 消费消息成功:{}。", ssoIds, message.toString());
} else {
log.info("新增用户:{} 消费消息失败:{}。", ssoIds, message.toString());
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
生产者发送消息
不同vhost换交换机和队列
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Slf4j
@Service
public class SendMessage {
@Resource(name = "oneRabbitTemplate")
private RabbitTemplate oneRabbitTemplate;
@Resource(name = "twoRabbitTemplate")
private RabbitTemplate twoRabbitTemplate;
@Value("${spring.rabbitmq.one.topic.exchange.name}")
private String oneTopicExchange;
@Value("${spring.rabbitmq.two.topic.exchange.name}")
private String twoTopicExchange;
@Value("${spring.rabbitmq.one.queue}")
private String oneRoutingKey;
@Value("${spring.rabbitmq.two.queue}")
private String twoRoutingKey;
public void sendToOneMessage(String msg) {
oneRabbitTemplate.convertAndSend(oneTopicExchange, oneRoutingKey, msg);
}
public void sendToTwoMessage(String msg) {
twoRabbitTemplate.convertAndSend(twoTopicExchange, twoRoutingKey, msg);
}
}
就先说到这
\color{#008B8B}{ 就先说到这}
就先说到这
在下
A
p
o
l
l
o
\color{#008B8B}{在下Apollo}
在下Apollo
一个爱分享
J
a
v
a
、生活的小人物,
\color{#008B8B}{一个爱分享Java、生活的小人物,}
一个爱分享Java、生活的小人物,
咱们来日方长,有缘江湖再见,告辞!
\color{#008B8B}{咱们来日方长,有缘江湖再见,告辞!}
咱们来日方长,有缘江湖再见,告辞!