1. RabbitMQ 的消息持久化处理
消息的可靠性是 RabbitMQ 的一大特色, 那么 RabbitMQ 是如何保证消息可靠性的呢——消息持久化
我们先来看个现象
新建两个项目,一个消费者一个生产者,设置queue的autoDelete为true,生产者循环发送消息,然后,运行消费者,然后运行生产者,如图;
info队列开始接收消息,再看管理台,如图:
两个队列,error和info,其中info队列在运行中并接收消息。
然后,我们关闭消费者,关闭之前观察控制台打印信息,如图,关闭的时候,消息接收到了8,
然后,在看管理台,发现info和error队列消失了如图:
接着,重启消费者,如图:
消费者接收到的消息是从29开始,也就是说9-28的消息丢失了。这是为什么了?很明显的,因为当消费者停止的时候,rabbitMQ的队列列表中已经不存在了info和error队列了,所以生产者发送的消息就成了游魂,无处安放,从而导致了消息的丢失,所以要解决这个问题,首要的任务就是保证消费者关闭的时候,rabbitMQ对应的队列不被删除就可以了。我们发现,在消费者配置中@Queue有一个属性autoDelete,我们设置为true,如图;
我们尝试着将info服务的queue的autoDelete为false,如图:
在运行消费者,然后,关闭消费者,如图:
消息在71的时候关闭了,再看管理台,info队列还在运行,但是error队列已经消失了,同时info的features没有AD(自动删除)标志了。如图:
重启消费者服务,消息在一瞬间从72开始发出来了,如图:
这样消息就没有丢失了。
总结:消息的持久化处理就是将queue的autoDelete设置为false
autoDelete 属性
我们发现autoDelete不但在@queue注解存在,在@Exchange中也有,两者有什么区别了?
1)@Queue: 当所有消费客户端连接断开后, 是否自动删除队列 true: 删除 false: 不删除
2)@Exchange: 当所有绑定队列都不在使用时, 是否自动删除交换器 true: 删除 false: 不删除
所以,两者都可以设置autoDelete=false达到消息持久化的目的
2. RabbitMQ 中的消息确认 ACK 机制
2.1 什么是消息确认ACK
如果在处理消息的过程中,消费者服务器在处理消息的时候出现异常,那么,可能这条正在处理的消息就没有完成消息消费,数据就会丢失,为了确保数据不会丢失,RabbitMQ支持消息确认——ACK
2.2 ACK消息确认机制
ACK机制是消费者从RabbitMA收到消息并处理完成后,反馈给RabbitMQ,RabbitMQ收到反馈后才将此消息从队列中删除。
1)如果一个消费者在处理消息的时候出现网络不稳定、服务器异常等现象,那么就不会有ACK反馈,RabbitMQ会认为这个消息没有被正常消费,从而会将队列重新放到队列中。
2)如果在集群的情况下,RabbitMQ会立即将这个消息推送给这个在线的其他消费者,这种机制保证了在消费者服务端故障的时候,不丢失任何消息和任务。
3)消息永远不会从RabbitMQ中删除,只有当消费者正确发送ACK反馈,RabbitMQ确认收到后,消息才会从RabbitMQ服务器的数据中删除
4)消息的ACK确认机制默认是打开的。
2.3 ACK机制的开发注意事项
如果忘记了ACK,那么后果很严重。当Consumer退出的时候,Message会一直重新分发,然后RabbitMQ会占用越来越多的内存,由于RabbitMQ会长时间运行,因此这个“内存泄漏”是致命的。
2.4 ACK的解决方法
我们先来通过一个实际现象来深刻的了解ACK问题
1)创建消费者工程,在消息处理中手动制造一个异常,如图:
2)运行消费者和生产
如图所示:
系统会一直运行,一直报错,同时消息会一直发送,在看管理台,如图:
队列中的消息不停的增加,一旦时间长了之后,很容易造成内存泄漏。
那么这个问题怎么解决了?
2.4.1 通过异常处理解决
如图:
再次运行消费者,就不会出现一直发送消息的情况了,在看管理台,如图:
消息被正常消费了,这样不会产生内存泄漏问题。
2.4.2 通过消息重试次数限定解决问题
在消费者服务,通过设置重试次数限制,可以达到当rabbitMQ发送消息的时候,可以根据我们配置的重试次数来重复发送消息,当程序遇到异常,rabbitMQ发送指定次数的消息之后,还没有ACK确认返回,就不会再次发送了。
在配置文件中开启重试:
#开启重试
spring.rabbitmq.listener.retry.enabled=true
#重试次数, 默认为 3 次
spring.rabbitmq.listener.retry.max-attempts=5
注意:这里的key是给spring读的,是固定值。