SpringBoot 整合 RabbitMQ 实现消息可靠传输
消息的可靠传输是面试必问的问题之一,保证消息的可靠传输主要在生产端开启
comfirm模式,RabbitMQ开启持久化,消费端关闭自动ack模式。
环境配置
SpringBoot 整合 RabbitMQ 实现消息的发送。
- 添加
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 添加 application.yml 配置文件
spring:
rabbitmq:
host: 192.168.3.19
port: 5672
username: admin
password: xxxx
- 配置交换机、队列以及绑定
@Bean
public DirectExchange myExchange() {
DirectExchange directExchange = new DirectExchange("myExchange");
return directExchange;
}
@Bean
public Queue myQueue() {
Queue queue = new Queue("myQueue");
return queue;
}
@Bean
public Binding binding() {
return BindingBuilder.bind(myQueue()).to(myExchange()).with("myRoutingKey");
}
- 生产发送消息
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send")
public String send(String message) {
rabbitTemplate.convertAndSend("myExchange","myRoutingKey",message);
System.out.println("【发送消息】" + message)
return "【send message】" + message;
}
- 消费者接收消息
@RabbitListener(queuesToDeclare = @Queue("myQueue"))
public void process(String msg, Channel channel, Message message) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String time = sdf.format(date);
System.out.println("【接收信息】" + msg + " 当前时间" + time);
- 调用生产端发送消息
hello,控制台输出:
【发送消息】hello
【接收信息】hello 当前时间2022-05-12 10:21:14
说明消息已经被成功接收。
消息丢失分析

一条消息的从生产到消费,消息丢失可能发生在以下几个阶段:
- 生产端丢失: 生产者无法传输到
RabbitMQ - 存储端丢失:
RabbitMQ存储自身挂了 - 消费端丢失:存储由于网络问题,无法发送到消费端,或者消费挂了,无法发送正常消费
RabbitMQ 从生产端、储存端、消费端都对可靠性传输做很好的支持。
生产阶段
生产阶段通过请求确认机制,来确保消息的可靠传输。当发送消息到 RabbitMQ 服务器 之后,RabbitMQ 收到消息之后,给发送返回一个请求确认,表示RabbitMQ 服务器已成功的接收到了消息。
- 配置
application.yml
spring:
rabbitmq:
# 消息确认机制 生产者 -> 交换机
publisher-confirms: true
# 消息返回机制 交换机 -> 队列
publisher-returns: true
配置
@Configuration
@Slf4j
public class RabbitConfig {
@Autowired
private ConnectionFactory connectionFactory;
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
log.info("【correlationData】:" + correlationData);
log.info("【ack】" + ack);
log.info("【cause】" + cause);
if (ack) {
log.info("【发送成功】");
} else {
log.info("【发送失败】correlationData:" + correlationData + " cause:" + cause);
}
}
});
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
log.warn("【消息发送失败】");
log.info("【message】" + message);
log.info("【replyCode】" + replyCode);
}
});
return rabbitTemplate;
}
}
消息从 生产者 到 交换机, 有confirmCallback 确认模式。发送消息成功后消息会调用方法confirm(CorrelationData correlationData, boolean ack, String cause),根据 ack 判断消息是否发送成功。
消息从 交换机 到 队列,有returnCallback 退回模式。
发送消息 product message 控制台输出如下:
【发送消息】product message
【接收信息】product message 当前时间2022-05-12 11:27:56
【correlationData】:null
【ack】true
【cause】null
【发送成功】
生产端模拟消息丢失
这里有两个方案:
- 发送消息后立马关闭 broke,后者把网络关闭,但是broker关闭之后控制台一直就会报错,发送消息也报500错误。
- 发送不存在的交换机:
// myExchange 修改成 myExchangexxxxx
rabbitTemplate.convertAndSend("myExchangexxxxx","myRoutingKey",message);
结果:
【correlationData】:null
【ack】false
【cause】channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'myExchangexxxxx' in vhost '/', class-id=60, method-id=40)
【发送失败】
当发送失败可以对消息进行重试
- 交换机正确,发送不存在的队列:
交换机接收到消息,返回成功通知,控制台输出:
【correlationData】:CorrelationData [id=7d468b47-b422-4523-b2a2-06b14aef073c]
【ack】true
【cause】null
【发送成功】
交换机没有找到队列,返回失败信息:
【消息发送失败】
【message】product message
【replyCode】312
RabbitMQ
开启队列持久化,创建的队列和交换机默认配置是持久化的。首先把队列和交换机设置正确,修改消费监听的队列,使得消息存放在队列里。
修改队列的持久化,修改成非持久化:
@Bean
public Queue myQueue() {
Queue queue = new Queue("myQueue",false);
return queue;
}
发送消息之后,消息存放在队列中,然后重启 RabbitMQ,消息不存在了。
设置队列持久化:
@Bean
public Queue myQueue() {
Queue queue = new Queue("myQueue",true);
return queue;
}
重启之后,队列的消息还存在。
消费端
消费端默认开始 ack 自动确认模式,当队列消息被消费者接收,不管有没有被消费端消息,都自动删除队列中的消息。所以为了确保消费端能成功消费消息,将自动模式改成手动确认模式:
修改 application.yml 文件
spring:
rabbitmq:
# 手动消息确认
listener:
simple:
acknowledge-mode: manual
消费接收消息之后需要手动确认:
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
@RabbitListener(queuesToDeclare = @Queue("myQueue"))
public void process(String msg, Channel channel, Message message) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
String time = sdf.format(date);
System.out.println("【接收信息】" + msg + " 当前时间" + time);
System.out.println(message.getMessageProperties().getDeliveryTag());
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} catch (IOException e) {
e.printStackTrace();
}
}
如果不添加:
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
发送两条消息
消息被接收后,没有确认,重新放到队列中:

重启项目,之后,队列的消息会发送到消费者,但是没有 ack 确认,还是继续会放回队列中。
加上 channel.basicAck 之后,再重启项目:

队列消息就被删除了
basicAck 方法最后一个参数 multiple 表示是删除之前的队列。
multiple 设置成 true,把后面的队列都清理掉了:

源码
https://github.com/jeremylai7/springboot-learning/tree/master/spring-rabbitmq
如果觉得文章对你有帮助的话,请点个推荐吧!
SpringBoot 整合 RabbitMQ 实现消息可靠传输的更多相关文章
- springboot整合rabbitmq,支持消息确认机制
安装 推荐一篇博客https://blog.csdn.net/zhuzhezhuzhe1/article/details/80464291 项目结构 POM.XML <?xml version= ...
- springboot整合rabbitMq实现消息延时发送
实现思路:利用mq的ttl设置消息失效时间 当达到设置时间后通过交换机到达死信队列中,消费者端绑定读取死信队列中信息来达到延时发送消息的功能. demo 如下: (1)在pom.xml 中引入rabb ...
- SpringBoot整合Rabbitmq设置消息请求头
String str = "{\"origin\":\"BBC\",\"origin_coupon_id\":51,\" ...
- SpringBoot系列八:SpringBoot整合消息服务(SpringBoot 整合 ActiveMQ、SpringBoot 整合 RabbitMQ、SpringBoot 整合 Kafka)
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合消息服务 2.具体内容 对于异步消息组件在实际的应用之中会有两类: · JMS:代表作就是 ...
- springboot整合rabbitmq实现生产者消息确认、死信交换器、未路由到队列的消息
在上篇文章 springboot 整合 rabbitmq 中,我们实现了springboot 和rabbitmq的简单整合,这篇文章主要是对上篇文章功能的增强,主要完成如下功能. 需求: 生产者在启 ...
- RabbitMQ入门到进阶(Spring整合RabbitMQ&SpringBoot整合RabbitMQ)
1.MQ简介 MQ 全称为 Message Queue,是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. 2.为什么要用 MQ 1.流量消峰 没使用MQ 使用了MQ 2.应用解耦 ...
- springboot学习笔记-6 springboot整合RabbitMQ
一 RabbitMQ的介绍 RabbitMQ是消息中间件的一种,消息中间件即分布式系统中完成消息的发送和接收的基础软件.这些软件有很多,包括ActiveMQ(apache公司的),RocketMQ(阿 ...
- 【SpringBoot系列5】SpringBoot整合RabbitMQ
前言: 因为项目需要用到RabbitMQ,前几天就看了看RabbitMQ的知识,记录下SpringBoot整合RabbitMQ的过程. 给出两个网址: RabbitMQ官方教程:http://www. ...
- 一篇学习完rabbitmq基础知识,springboot整合rabbitmq
一 rabbitmq 介绍 MQ全称为Message Queue,即消息队列, RabbitMQ是由erlang语言开发,基于AMQP(Advanced MessageQueue 高级消息队列协议 ...
随机推荐
- H5打造3d场景不完全攻略(一): H5 3d表现形式
前言 日前,taobao造物节H5放肆地火了一把.相信接下来将3d嵌入网站的这种营销方式会被越来越多的人留意到.工作之余体验了若干个3d H5页面,感觉这类的H5互动体验性明显要比普通的要强,把二维的 ...
- Web Storage相关
访问原文地址 概述 DOM存储的机制是通过存储字符串类型的键/值对,来提供一种安全的存取方式.这个附加功能的目标是提供一个全面的,可以用来创建交互式应用程序的方法(包括那些高级功能,例如可以离线工作一 ...
- Linux 0.11源码阅读笔记-总览
Linux 0.11源码阅读笔记-总览 阅读源码的目的 加深对Linux操作系统的了解,了解Linux操作系统基本架构,熟悉进程管理.内存管理等主要模块知识. 通过阅读教复杂的代码,锻炼自己复杂项目代 ...
- python正则表达式替换或去除指定字符
代码: import re regEx = "[\n""|]" # 去除字符串中的换行符.中文冒号.|,需要去除什么字符就在里面写什么字符 str= re.su ...
- PAT B1024科学计数法
科学计数法是科学家用来表示很大或很小的数字的一种方便的方法,其满足正则表达式 [+-][1-9].[0-9]+E[+-][0-9]+,即数字的整数部分只有 1 位,小数部分至少有 1 位,该数字及其指 ...
- 界面跳转+信息传递+AS中如何将ADV转移到其他盘中
今日所学:界面跳转 信息传递 遇到的问题: 昨天遇到不能新建java类,在网上百度了很多,大多原因是没有新建java类的模板,但是我有,换了一个新的新建的方式后,发现虽然能建立了,但在测试时还是不能页 ...
- Math类、Random类、System类、BigInteger类、BigDecimal类、Date类、SimpleDateFormat、Calendar类
Math类* A:Math类概述 * Math 类包含用于执行基本数学运算的方法,如初等指数.对数.平方根和三角函数. * B:成员方法 * public static int abs(int a) ...
- 自己对kmp算法的理解,借由 28. 实现 strStr() 为例
做题思路 or 感想 : 就借由这道题来理解一下kmp算法吧 kmp算法的操作过程我觉得有句话很合适 :KMP 算法永不回退 目标字符串 的指针 i,不走回头路(不会重复扫描 目标字符串),而是借助 ...
- Vue src动态引入图片不显示问题
使用vue动态引入图片显示失败,查看控制台,发现图片返回类型为text/html,这里我的图片是从后台服务器上获取的,如果你没有使用Vue的devServer.proxy 进行代理,可以光速移步百度 ...
- Java报错:Error creating bean with name 'testController': Injection of resource dependencies failed
报错如下: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testCo ...