RabbitMQ-从基础到实战(2)— 防止消息丢失
转载请注明出处
0.目录
RabbitMQ-从基础到实战(1)— Hello RabbitMQ
1.简介
RabbitMQ中,消息丢失可以简单的分为两种:客户端丢失和服务端丢失。针对这两种消息丢失,RabbitMQ都给出了相应的解决方案。
2.防止客户端丢失消息

如图,生产者P向队列中生产消息,C1和C2消费队列中的消息,默认情况下,RabbitMQ会平均的分发消费给C1C2(Round-robin dispatching),假设一个任务的执行时间非常长,在执行过程中,客户端挂了(连接断开),那么,该客户端正在处理且未完成的消息,以及分配给它还没来得及执行的消息,都将丢失。因为默认情况下,RabbitMQ分发完消息后,就会从内存中把消息删除掉。
3.消息确认(Message acknowledgment)
为了解决上述问题,RabbitMQ引入了消息确认机制,当消息处理完成后,给Server端发送一个确认消息,来告诉服务端可以删除该消息了,如果连接断开的时候,Server端没有收到消费者发出的确认信息,则会把消息转发给其他保持在线的消费者。
验证上述问题
首先,我们验证上述问题(客户端丢失消息)是否真的存在,对Consumer进行如下改造。

先生产两条消息

启动消费者,在消费者接收到消息,还没处理完成的时候,强制关掉

这时,观察控制台,发现两条消息都没有了,1条是在执行中丢失的,还有1条,已经分配给这个Consumer,还没来得及处理,也丢失了

这证明了上述问题是真的存在的,如果发生在生产环境,将产生难以预料的后果
引入消息确认机制
为了方便观察,我们用CMD来运行Consumer,要通过maven打成可执行的JAR包,需要在pom.xml中增加如下配置
<build>
<finalName>Consumer</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.liyang.ticktock.rabbitmq.App</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>assembly</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin> </plugins>
</build>
上述配置描述了最终打包名字、入口类路径、带上依赖包、使用1.8版本的JDK进行打包,配置完后,就可以通过maven的install方法,在target目录生成可执行的jar包,如果包大小很小,应检查配置,是不是没有带上依赖包

再次改造Consummer类

install成可执行jar包,通过cmd开启两个consumer

通过Sender发送一条消息,然后用Ctrl+C结束先收到消息的Consumer,发现另外一个Consumer接收到了未处理完的消息

问题得到了解决,现在消费者在执行过程中死掉也不会丢失消息了
看一下发送确认的方法
/**
* Acknowledge one or several received
* messages. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}
* or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method
* containing the received message being acknowledged.
* @see com.rabbitmq.client.AMQP.Basic.Ack
* @param deliveryTag the tag from the received 这个是RabbitMQ用来区分消息的,文档在这 * @param multiple true to acknowledge all messages up to and 为true的话,确认所有消息,为false只确认当前消息
* including the supplied delivery tag; false to acknowledge just
* the supplied delivery tag.
* @throws java.io.IOException if an error is encountered
*/
void basicAck(long deliveryTag, boolean multiple) throws IOException;
在官方文档中,这样描述deliveryTag

简单来说,就是RabbitMQ内部用来区分消息的一个标签,从envelope中获取就行了
忘记确认将引起内存泄漏
RabbitMQ只有在收到消费者确认后,才会从内存中删除消息,如果消费者忘了确认(更多情况是因为代码问题没有执行到确认的代码),将会导致内存泄漏
验证一下
注释掉Consumer中的确认代码


运行Sender和Consumer,不停的生产消费消息,发现消费者在正常的消费消息

查看控制台,发现已经被吃掉了43KB的内存,所以,在试用过程中,一定要保证消息确认在任何情况下都可以发出,否则即使消费者处理完成,RabbitMQ也不会把消息在内存中清除,在该消费者断开连接之后,还会把消息转发给其他消费者重新处理,将引发难以预计的问题


4.消息的持久化
现在,消费者宕机已经无法影响到我们的消息了,但如果RabbitMQ重启了,消息依然会丢失。所幸的是,RabbitMQ提供了持久化的机制,将内存中的消息持久化到硬盘上,即使重启RabbitMQ,消息也不会丢失。但是,仍然有一个非常短暂的时间窗口(RabbitMQ收到消息还没来得及存到硬盘上)会导致消息丢失,如果需要严格的控制,可以参考官方文档
要使用RabbitMQ的消息持久化,在声明队列时设置一个参数即可

注意,RabbitMQ不允许对一个已经存在的队列用不同的参数重新声明,对于试图这么做的程序,会报错,所以,改动之前代码之前,要在控制台中把原来的队列删除

重新声明队列后,发现Durable为true

重启RabbitMQ

队列的消息没有丢失

5.结束语
这一章介绍了RabbitMQ消息的确认和持久化,后面将会继续深入介绍RabbitMQ的其他特性
RabbitMQ-从基础到实战(2)— 防止消息丢失的更多相关文章
- RabbitMQ,RocketMQ,Kafka 事务性,消息丢失和消息重复发送的处理策略
消息队列常见问题处理 分布式事务 什么是分布式事务 常见的分布式事务解决方案 基于 MQ 实现的分布式事务 本地消息表-最终一致性 MQ事务-最终一致性 RocketMQ中如何处理事务 Kafka中如 ...
- RabbitMQ-从基础到实战(3)— 消息的交换
1.简介 在前面的例子中,每个消息都只对应一个消费者,即使有多个消费者在线,也只会有一个消费者接收并处理一条消息,这是消息中间件的一种常用方式.还有另外一种方式,生产者生产一条消息,广播给所有的消费者 ...
- RabbitMQ-从基础到实战(4)— 消息的交换(下)
0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换(上) 1.简介 ...
- RabbitMQ-从基础到实战(5)— 消息的交换(下)
转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换 ...
- RabbitMQ-从基础到实战(3)— 消息的交换(上)
转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(2)— 防止消息丢失 RabbitMQ-从基础到实战(4)— 消息的交换 ...
- RabbitMQ-从基础到实战(6)— 与Spring集成
0.目录 RabbitMQ-从基础到实战(1)- Hello RabbitMQ RabbitMQ-从基础到实战(2)- 防止消息丢失 RabbitMQ-从基础到实战(3)- 消息的交换(上) Rabb ...
- RabbitMQ防止消息丢失
转载请注明出处 0.目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(3)— 消息的交换 1.简介 RabbitMQ中,消息丢失可以简单的分为 ...
- RabbitMQ-从基础到实战(1)— Hello RabbitMQ
转载请注明出处 1.简介 本篇博文介绍了在windows平台下安装RabbitMQ Server端,并用JAVA代码实现收发消息 2.安装RabbitMQ RabbitMQ是用Erlang开发的,所以 ...
- RabbitMQ实战:理解消息通信
RabbitMQ是一个开源的消息代理和队列服务器,可以通过基本协议在完全不同的应用之间共享数据,可以将作业排队以便让分布式服务进行处理. 本篇介绍下消息通信,首先介绍基础概念,将这些概念映射到AMQP ...
随机推荐
- css 清除浮动的方法
/*方法一*/ /*局部清除*/ ;visibility:hidden;display:block;clear:both;} .clr{display:inline-block;} .clr{disp ...
- CSS页面渲染优化属性will-change
前面的话 当我们通过某些行为(点击.移动或滚动)触发页面进行大面积绘制的时候,浏览器往往是没有准备的,只能被动使用CPU去计算与重绘,由于没有事先准备,应付渲染够呛,于是掉帧卡顿.而CSS属性wi ...
- Ubuntu14.04 安装Oracle JDK
在Ubuntu 14.04 中安装 Oracle JDK6,7 或 8. 准备:添加add-apt-repository工具 #apt-get install python-software-prop ...
- Centos6.5 mysql折腾记
1.yum安装mysql [root@localhost ~]# yum -y install mysql-server 安装结果 Installed: mysql-server.x86_64 0:5 ...
- js详解之作用域-实例
函数如下大家可以做做看 function aa(a,b,c){ function a(){} console.log(a); console.log(aa); console.log(argument ...
- Brainfuck与Ook!编程语言解析与解密
MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...
- c++ 继承类强制转换时的虚函数表工作原理
本文通过简单例子说明子类之间发生强制转换时虚函数如何调用,旨在对c++继承中的虚函数表的作用机制有更深入的理解. #include<iostream> using namespace st ...
- iOS程序生命周期 AppDelegate
iOS的应用程序的生命周期,还有程序是运行在前台还是后台,应用程序各个状态的变换,这些对于开发者来说都是很重要的. iOS系统的资源是有限的,应用程序在前台和在后台的状态是不一样的.在后台时,程序会受 ...
- LAMP学习小记
记录今天学习到的解决LAMP环境搭建的两个小问题: 问题1.xshell无法连接到虚拟机上的linux主机 解决方法: (1)进入网络配置文件: vi /etc/sysconfig/network-s ...
- 从C#到TypeScript - Promise
总目录 从C#到TypeScript - 类型 从C#到TypeScript - 高级类型 从C#到TypeScript - 变量 从C#到TypeScript - 接口 从C#到TypeScript ...