总结一下消息队列面试过程中遇到的问题,各个互联网公司对于消息队列还是很感兴趣的,问的问题很多,深度也够,一遍面试一边学习的效率其实很高,希望可以通过整理更加了解ActiveMQ这个消息队列,后续计划对RocketMQ,RabbitMQ,kafka学习一下。
ActiveMQ的存储机制
- 通常情况下,非持久化消息时存储在内存中的,持久化消息时存储在日志文件中的,最大限制可以配置。
- 内存中的非持久化消息堆积到一定程度会影响内存的空间,此时ActiveMQ会将内存中的非持久化消息写入临时文件中,以释放内存。重启后非持久化的临时文件会直接删除。
ActiveMQ挂掉怎么办
ActiveMQ采用持久化方案的时候,当日志文件达到最大限制时会造成生产者阻塞,此时生产者不可以再生产新的服务,但是消费者可以正常连接并消费消息,当消费掉一部分消息之后,文件删除之后生产者可以继续生产消息,服务会自动回复正常运行。
ActiveMQ采用非持久化方案的时候,当临时文件大小达到上限时会产生生产者阻塞,消费者可以正常连接却不能正常消费消息,整个系统可连接,但是无法提供服务,ActiveMQ挂掉。
解决方案:尽量不要用非持久化消息,如果要用的话,需要将临时文件限制尽可能调大。
为什么选择ActiveMQ?
- 降低分布式系统之间的耦合度
- 产品很成熟,在很多公司得到了应用
- 文档众多,协议支持的很好,有封装好的Java客户端
- ActiveMQ性能不如RabbitMQ,kafka等,后期计划更新消息队列。
ActiveMQ丢失消息怎么处理?
- ActiveMQ每隔10s会发送一个心跳包,用来检测客户端是否存活。而如果采用非持久化方案的消息队列,消息堆积之后会触发一次写过程,这个过程会阻塞所有的活动,持续20-30s,并且会随着内存增大而增加。
- 此时客户端发完消息会关闭连接,发送的消息存在服务端的缓存里面,服务器直接读取缓存时不会造成消息丢失的,但是由于心跳包的存在,发生了SocketException异常,缓存区数据失效,从而造成消息丢失。
- 解决方案:使用持久化消息,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务之后,commit方法会负责等待服务器的返回,不会关闭连接导致消息丢失。
消息的不均匀消费问题?
- ActiveMQ的prefetch机制,消费者去获取消息时,会一次获取一批,默认1000。这些消息在没被消费之前,管理控制台可以看见这些消息,但是无法将其分配给其他消费者。消费成功,在服务器端删除对应消息,消费失败,退回服务端重新分配。
- 解决方案,将prefetch设置为1
死信队列
由于AUTO_ACKNOWLWDGE只是确保消费者收到消息,不保证消息能够正确执行。因此实际中一般采用CLIEND_ACKNOWLEDGE,自己去处理什么时候返回确认信息。
使用了AUTO_ACKNOWLWDGE时,如果消费消息采用的是consumer.receive()方法,则直接确认。但是如果采用Listener回调函数,则消息到达会执行Listener接口的onMessage方法。这种情况下,只有执行完onMessage方法才会确认消息。此时如果报错,消息不会被删除,而是退回服务器。重试6次之后,会进入死信队列。
ActiveMQ中的消息重发时间间隔和重发次数
- 消息接受者在处理完一条消息的处理过程后没有对MOM进行应答,则该消息由MOM重发。
- 如果某个队列设置了预读参数,如果消息接受者在处理第一条消息时就宕机了,则预读数量的所有消息都会被重发。
- 如果session是事务的,只要消息接受者有一条消息没有确认,或者发送期间MOM或者客户端宕机,则该事务范围中的所有消息都将重发。
- 重发侧率可以自定义配置,比如配置最大重传数量,默认为6,最大传送延迟等。。。