环境配置
Windows11 | |
rabbitmq版本:3.12.5(重要) | |
Erlang版本:Erlang/OTP 25(重要) | |
注意:rabbitmq和Erlang的版本必须匹配,否则会有不必要的麻烦(重要) | |
JDK版本:11 | |
Maven版本:maven-3.9.4 | |
springboot版本:v2.7.7 | |
数据库MySQL版本:8.0.26 |
数据库脚本
sys_mq_config:用于交换机、队列相关配置
mq_faild_retry:MQ消息消费失败,用于重试消费
CREATE TABLE `sys_mq_config` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '逻辑ID',
`exchange` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'MQ交换机',
`queue` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '队列名称',
`mq_status` varchar(3) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '状态',
`create_by` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '创建人',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`mq_version` int DEFAULT '1',
`mq_type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT 'mq类型(direct、topic、fanout、headers)',
`mq_durable` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '持久化',
`mq_auto_delete` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '自动删除',
`route_key` varchar(255) DEFAULT NULL COMMENT '路由键',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='用于配置MQ动态队列';
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (1, 'next.big.a', 'next.big.a.1', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.1');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (2, 'next.big.a', 'next.big.a.1', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.1');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (3, 'next.big.a', 'next.big.a.2', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.2');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (4, 'next.big.a', 'next.big.a.3', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.3');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (5, 'next.big.a', 'next.big.a.4', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.4');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (6, 'next.big.a', 'next.big.a.5', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.5');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (7, 'next.big.a', 'next.big.a.6', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.6');
INSERT INTO `sys_mq_config` (`id`, `exchange`, `queue`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `version`, `mq_type`, `mq_durable`, `mq_autoDelete`, `route_key`) VALUES (8, 'next.big.a', 'next.big.a.7', 'A', 'admin', '2022-08-30 16:44:30', 'admin', '2022-08-30 16:44:30', 1, 'direct', 'true', 'false', 'next.big.a.7');
CREATE TABLE `mq_faild_retry` (
`id` int NOT NULL AUTO_INCREMENT,
`message` longtext COLLATE utf8_bin COMMENT '消息内容',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (6, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (7, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"343355c1-ef92-4cbb-9f5c-2ace0a1505eb"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (8, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"66d2e539-e7c6-4dc8-a16e-bd4393f33c17"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (9, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"22591adf-734c-4e89-9e68-45db1a79b489"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (10, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"cd0a2940-e2c5-4d24-b459-93cb5f3bc791"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (11, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"ab030d27-3134-43f5-ab79-99778ae29ff9"}');
INSERT INTO `mq_faild_retry` (`id`, `message`) VALUES (12, '{"msg":"世界上那么多纷纷扰扰,能真正和你产生关系的不多;外面那么冷,你更要记住那个帮你暖被窝的。","qid":"81458778-6fd4-44ae-9ecf-536868cc8b4b"}');
RabbitUtil工具类
注:此类功能包含,初始化交换机 队列配置、初始化监听列表、获取已创建的队列、判断是否存在队列、将队列添加到监听列表
获取监听列表、判断该队列是否存在监听列表中、创建交换机 创建队列 交换机绑定队列(直连交换机、主题交换机、扇形交换机、头部交换机)
import com.next.common.mq.entity.SysMqConfig;
import com.next.common.mq.mapper.SysMqConfigMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.List;
/**
* 自定义MQ工具类
* */
@Component
public class RabbitUtil {
@Autowired
RabbitAdmin rabbitAdmin;
/**
* 消息监听
* */
@Autowired
SimpleMessageListenerContainer listenerContainer;
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
SysMqConfigMapper sysMqConfigMapper;
/**
* 初始化交换机 队列配置
* */
public void initMqConfig(){
List<SysMqConfig> sysMqConfigs = sysMqConfigMapper.selectList(null);
if (!CollectionUtils.isEmpty(sysMqConfigs)){
sysMqConfigs.forEach(item ->{
if (item.getMqStatus().equals("A")){//启用状态
//不存在则创建队列
if (!isExistQueue(item.getQueue())){
bindQueueAndExchange(
item.getExchange(),
item.getQueue(),
item.getMqType(),
item.getRouteKey(),
Boolean.valueOf(item.getMqDurable()),
Boolean.valueOf(item.getMqAutoDelete()
)
);
}
}
});
}
}
/**
* 初始化监听列表
* */
public void initListenerQueue(){
List<SysMqConfig> sysMqConfigs = sysMqConfigMapper.selectList(null);
if (!CollectionUtils.isEmpty(sysMqConfigs)){
sysMqConfigs.forEach(item ->{
//不存在则添加
if (!isExistListenerQueue(item.getQueue())){
addListenerQueue(item.getQueue());
}
});
}
}
/**
* 获取已创建的队列
* */
public QueueInformation getQueueInfo(String queueName){
return rabbitAdmin.getQueueInfo(queueName);
}
/**
* 判断是否存在队列
* */
public boolean isExistQueue(String queueName){
try{
if (StringUtils.isEmpty(getQueueInfo(queueName).getName())){
return false;
}
}catch (Exception e){
return false;
}
return true;
}
/**
* 将队列添加到监听列表
* */
public void addListenerQueue(String queueName){
listenerContainer.addQueueNames(queueName);
}
/**
* 获取监听列表
* */
public String[] getListenerQueue(){
return listenerContainer.getQueueNames();
}
/**
* 队列是否存在监听列表中
* */
public boolean isExistListenerQueue(String queueName){
return CollectionUtils.arrayToList(listenerContainer.getQueueNames()).contains(queueName);
}
/**
* 创建交换机 创建队列 交换机绑定队列
* */
public void bindQueueAndExchange(String exchangeName,String queueName,String exchangeType,String routeKey,Boolean durable,boolean autoDelete){
//声明交换机
switch (exchangeType){
//直连交换机
case "direct":
createDirect(exchangeName,queueName,routeKey,durable,autoDelete);
break;
//主题交换机
case "topic":
createTopic(exchangeName,queueName,routeKey,durable,autoDelete);
break;
//扇形交换机
case "fanout":
createFanout(exchangeName,queueName,durable,autoDelete);
break;
//头部交换机
case "headers":
createHeaders(exchangeName,queueName,durable,autoDelete);
break;
default:
throw new RuntimeException("交换机类型错误");
}
}
/**
* 直连交换机
* */
public void createDirect(String exchangeName,String queueName,String routeKey,Boolean durable,boolean autoDelete){
DirectExchange directExchange = new DirectExchange(exchangeName, durable, autoDelete);
rabbitAdmin.declareExchange(directExchange);
//声明队列
Queue queue = new Queue(queueName);
rabbitAdmin.declareQueue(queue);
//注册交换机队列
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(directExchange).with(routeKey));
}
/**
* 主题交换机
* */
public void createTopic(String exchangeName,String queueName,String routeKey,Boolean durable,boolean autoDelete){
TopicExchange topicExchange = new TopicExchange(exchangeName, durable, autoDelete);
rabbitAdmin.declareExchange(topicExchange);
//声明队列
Queue queue = new Queue(queueName);
rabbitAdmin.declareQueue(queue);
//注册交换机队列
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(topicExchange).with(routeKey));
}
/**
* 扇形交换机
* */
public void createFanout(String exchangeName,String queueName,Boolean durable,boolean autoDelete){
FanoutExchange fanoutExchange = new FanoutExchange(exchangeName, durable, autoDelete);
rabbitAdmin.declareExchange(fanoutExchange);
//声明队列
Queue queue = new Queue(queueName);
rabbitAdmin.declareQueue(queue);
//注册交换机队列
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(fanoutExchange));
}
/**
* 头部交换机
* */
public void createHeaders(String exchangeName,String queueName,Boolean durable,boolean autoDelete){
HeadersExchange headersExchange = new HeadersExchange(exchangeName, durable, autoDelete);
rabbitAdmin.declareExchange(headersExchange);
//声明队列
Queue queue = new Queue(queueName);
rabbitAdmin.declareQueue(queue);
//注册交换机队列
HashMap<String, Object> header = new HashMap<>();
header.put("queue", "*");
header.put("bindType", "whereAll");
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(headersExchange).whereAll(header).match());
}
}
RabbitMqInit 初始化MQ功能类
注:使用@PostConstruct @Async注解,应用启动过程中初始化MQ配置
import com.next.common.mq.util.RabbitUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Slf4j
@Component
public class RabbitMqInit {
@Autowired
RabbitUtil rabbitUtil;
/**
* 初始化交换机 队列配置
* */
@PostConstruct
@Async
public void initMq(){
log.info("执行初始化MQ队列.....");
//初始化配置队列
rabbitUtil.initMqConfig();
//初始化监听列表
rabbitUtil.initListenerQueue();
}
}
RabbitMqConfig配置类
注:设置监听器属性、配置消息确认机制
import com.next.common.mq.listen.RabbitMqListener;
import lombok.SneakyThrows;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.ReturnedMessage;
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.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMqConfig {
@Autowired
private RabbitMqListener messageListener;
@Autowired
private CachingConnectionFactory connectionFactory;
@Bean
public SimpleMessageListenerContainer listenerContainer(ConnectionFactory connectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setConcurrentConsumers(1);
container.setMaxConcurrentConsumers(1);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
//设置监听处理器
container.setMessageListener(messageListener);
return container;
}
@Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(connectionFactory);
}
@Bean
public RabbitTemplate rabbitTemplate(){
RabbitTemplate rabbitTemplate = rabbitAdmin().getRabbitTemplate();
//开启Mandatory出发回调函数
rabbitTemplate.setMandatory(true);
//消息无论是否抵达交换机都执行,ack成功为true,否则为false
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("确认消息送到交换机(Exchange)结果:");
System.out.println("相关数据:" + correlationData);
System.out.println("是否成功:" + ack);
System.out.println("错误原因:" + cause);
}
});
//消息没有抵达队列执行,抵达队列不执行
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@SneakyThrows
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("确认消息送到队列(Queue)结果:");
System.out.println("发生消息:" + new String(returnedMessage.getMessage().getBody(),"utf8"));
System.out.println("回应码:" + returnedMessage.getReplyCode());
System.out.println("回应信息:" + returnedMessage.getReplyText());
System.out.println("交换机:" + returnedMessage.getExchange());
System.out.println("路由键:" + returnedMessage.getRoutingKey());
}
});
return rabbitTemplate;
}
}
SysMqService服务类
注:消息发送服务类
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.next.common.mq.mapper.SysMqConfigMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@Service
public class SysMqService {
@Autowired
SysMqConfigMapper sysMqConfigMapper;
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
RedisTemplate redisTemplate;
public String sendMQ(JSONObject param){
String msg = param.getString("msg");
String ex = param.getString("ex");
String queue = param.getString("queue");
//设置全局唯一消息ID
String qid = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(qid,"0");
redisTemplate.expire(qid,10, TimeUnit.MINUTES);
Map<String, Object> headerValues = new HashMap<>();
headerValues.put("msg",msg);
headerValues.put("qid",qid);
/**
* 声明消息 (消息体, 消息属性)
*/
MessageProperties messageProperties = new MessageProperties();
// 设置消息是否持久化。Persistent表示持久化,Non-persistent表示不持久化
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
messageProperties.setContentType("UTF-8");
messageProperties.getHeaders().putAll(headerValues);
Message qMessage = null;
try {
qMessage = new Message(JSONUtil.toJsonStr(headerValues).getBytes("UTF-8"), messageProperties);
rabbitTemplate.convertAndSend(ex,queue,qMessage);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return "success";
}
}