Java面试篇【RabbitMQ】常见面试题(2024最新)

RabbitMQ

1.为什么使用MQ?优点是什么

因为MQ可以异步处理,提高系统吞吐量。

应用解耦,系统之间可以通过消息通信,不关心其他系统的处理。

流量削峰,可以通过消息队列的长度,控制请求量。可以缓解短时间内的高并发请求。

解耦 :A->BCD,但是如果E也想要这个数据,或者C不想要了。A就需要修改。与各种下游系统严重耦合。

如果使用MQ,A系统产生一条消息,到MQ,哪个系统要消息就去消息系统里去消费。
异步 :A接收请求,在本地写库,在BCD三个系统分别要进行写库,本地很快,其它系统完成后传过来很慢,如果同步方式,

A需要BCD都完成之后,才能返回给前端,用户会觉得好慢。如果使用异步化处理的话,A本地写库完成,再往消息队列里写一个数据,就可以返回了。
削峰:大流量来的时候,系统可能承受不住这么大的压力,如果用MQ,就可以对这些请求不用急着处理,转化为MQ里的消息数据,然后以恒定的速率吐出。

2.消息队列有什么缺点?

系统可用性降低:MQ挂了,系统就挂了,增加环节就是在增加风险。

复杂性提高:要考虑的问题多了:比如一致性问题(A的消息给BCD用,BC成功,D失败,就是不一致了),如何保证消息不被重复消费,如何保证消息可靠性传输。

3. 不同的MQ的对比?

  1. ActiveMQ 老牌消息中间件,很多公司运用的很广泛,功能强大,但是互联网公司的高并发扛不住。一般在传统企业,做异步调用,系统解耦。
  2. RocketMQ 阿里开源的,超高并发、高吞吐,性能好,基于java。
  3. rabbitMQ 支持高并发,高吞吐,性能很高,同时有完善的后台管理界面。还支持集群化,高可用部署架构,功能完善。但是缺点是基于erlang开发,不好研究源码。
  4. kafka功能少,优势在于专为超高吞吐量的实时日志采集,实时数据同步,实时数据计算等场景来设计。用在日志或者大数据计算方面比较多。

4.为啥使用rabbitMq不用RocketMQ?

rocketMQ不够稳定,社区活跃度不高,rabbitMQ很稳定。

5.MQ有哪些常见问题?如何解决?

  1. 消息的顺序性问题
  • 单线程化。比如一个订单的各种状态,下单,付款,发货,如果有多个队列,要按照id保证进入一个队列。
  • 一个队列对应一个消费者,这样就能保证顺序。
  • 或者一个队列可以对应多个消费者,但是消费者在业务逻辑内部要做判断,如果订单还在下单阶段就收到了发货的消息,那就只能抛出异常,回复nack,让rabbitMQ重发。
  1. 消息的重复问题
  • 为了确保每条消息至少成功一次的原则,队列里可能一条消息发了多次,需要在业务中做幂等性处理。
    • 查询和删除操作天然幂等。
    • 增改过程需要特殊处理。
  • 举个例子,论坛发帖,在打开编辑页面的时候就产生一个token,服务端存在redis,前端也存一份,然后发送消息的时候,看看redis里有没有这个token,有就新增,同时把redis的token删掉,后面的消息拿着同样的token找不到redis的对应token,就是重复消费,就丢弃。
  • 再举个例子,比如一个订单的各种状态,下单,付款,发货,一个字段记录这些状态,1->2->3.
  • 每次的消息就更改状态,消息重复过来了发现状态已经变了,那就不再做任何事情。
  • 订单新增的时候,要在消息里,订单里有唯一的id,可以用uuid,用雪花算法,redis原子自增,等等方式实现。

6. RabbitMQ

一个开源的,erlang编写的,基于AMQP协议的消息中间件

6.1 RabbitMQ的基本概念

  1. Producer:数据发送方,一般一个消息有两个部分,有效载荷和标签,用标签定位到把消息发送到哪个消费者
  2. Broker: 消息队列服务器实体
  3. Exchange: 消息交换器,从生产者那里收到消息,指定一个routing key,来指定这个消息的路由规则,routing key要和exchange type还有binding key联合使用生效。
  4. binding:绑定,绑定exchange和queue的同时,一般会指定一个binding key。
  5. queue 队列 一个message可以被同时拷贝到多个queue中。
  6. Connection 一个TCP连接,producer和consumer都是通过tcp连接到rabbitMQ server的。
  7. channel是建立在上述的tcp连接中的虚拟连接,AMPQ连接, 如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。
  8. Consumer 消费方
  9. virtual host 虚拟主机
  10. 多个不同的用户使用一个rabbitmq server 提供的服务时,可以划分出多个vhost,每个用户在自己的vhost里面建立exchange/queue.

6.2 RabbitMQ的工作模式

  1. direct
    消息的routing key与exchange的某个binding key完全符合,则转发到对应的队列中。
  2. fanout
    如果exchange 收到消息,就广播到所有绑定的队列上
  3. topic
    不需要完全匹配,而是符合匹配规则的路由规则即可。#代表任意个单词,*代表一个单词

6.3 ack机制

  1. 有手动和自动ack的方式,一般采用手动。autoAck = false;
  2. 服务端在收到ack信号之后才会删除内存(或者磁盘)中的消息,其实是先标记,再删除。
  3. 队列中有两种消息,一部分是还没有投递出去的消息,一部分已经投递但是还没得到确认的消息。
  4. 如果没有收到确认,消息不会有过期时间,所以也就是给消费者充足的处理时间。只有检测到消费者已经断开了,才会将消息重新放入队列,等待投递给下一个消费者。
  5. 除了ack,还可以拒绝消息,void basicReject(long deliveryTag,boolean requeue);
  6. requeue true重新放入队列,false直接丢弃

6.4 TTL过期时间

  1. 有队列过期时间,还有消息过期时间,以两者较小为准,超过了就过期。
  2. DLX Dead-Letter-Exchange,死信交换机,当一个消息变成死信,就可以发送到另一个交换机,就是死信交换机。
  3. 变成死信三种情况,过期,被拒绝,队列达到最大长度。
  4. 可以用来分析那些被拒绝的消息,转到新的交换机再用新的消费者程序去分析。

6.5 延迟队列

  1. 消息放入队列之后,并不会立即被消费,等待一定时间再消费。
  2. 场景:下单之后,十分钟不支付将取消订单,订单信息先发送到正常队列里。然后过了时间之后,再转到另一个交换机-->死信交换机这里再去真正消费。

6.6 生产者确认

如何确认消息成功的发送到了rabbitMQ的Exchange?

可以使用事务机制,但是,性能太低了。

java 复制代码
try{
    channel.txSelect();
    channel.basicPublish(xxx);
    channel.txCommit();
}catch(Exception e){
    e.printStackTrace();
    channel.txRollBack();
}


生产者确认

生产者将channel设置为confirm模式,一旦信道进入comfirm模式,所有在该信道上的消息就会有一个从1开始的编号,deliveryTag,消息到达匹配的队列之后,rabbitMQ会发送Basic.ACK给生产者,包含那个编号,生产者就知道了。

事务机制是阻塞的,生产者确认机制是异步的。生产者收到ACK之后,可以在回调中处理,收到NACK也可以在另一个回调逻辑中处理。

7. 如何保证RabbitMQ消息的可靠传输?

  1. 不可靠一般的原因就是丢了。
  2. 丢失可能是生产者丢失,消息列表丢失、消费者丢失。
  3. 生产者丢失就采用事务或者异步confirm机制,没收到confirm或者收到nack就重发了。这可能导致消息重复。
  4. 消息队列丢失数据:需要消息持久化,配合confirm,持久化磁盘之后再ack,在这之前有问题那么生产者会重发,在这之后有问题,反正已经持久化了有备份。
  5. 消费者丢失,把ack从自动改为手动,处理成功之后ack。造成的消息重复问题,一般需要消费者业务上做幂等性处理。

8.如何保证RabbitMQ的高可用?

为了确保RabbitMQ的高可用性,通常需要考虑以下几个方面:

  1. 集群部署(Clustering)

    将多个RabbitMQ节点配置成集群,可以提供负载均衡和故障转移的能力。在集群模式下,消息队列可以分布在多个节点上,提高系统的吞吐量。如果一个节点失败,其他节点仍然可以继续工作。

  2. 镜像队列(Mirrored Queues)

    在RabbitMQ集群中,可以设置镜像队列来确保队列中的消息被复制到多个节点。如果一个节点崩溃,队列的其他副本可以接管,保证消息不丢失。这是通过设置队列的x-ha-policy参数来实现的。

  3. 持久化(Persistence)

    对消息和队列进行持久化可以保护数据不会因为服务器崩溃而丢失。将消息标记为持久化(通过设置消息的delivery_mode属性为2),并确保队列也被声明为持久化,可以提高数据的可靠性。

  4. 确认和事务机制(Acknowledgments and Transactions)

    使用消息确认机制可以保证消息被正确处理。消费者处理完消息后发送ack(确认)给RabbitMQ,若RabbitMQ没有收到确认,它会将消息重新分发给其他消费者。此外,RabbitMQ还支持事务性消息处理。

  5. 监控和报警(Monitoring and Alerting)

    监控RabbitMQ的运行状况并设置相应的警报机制,可以及时发现并解决潜在的问题。可以利用RabbitMQ提供的管理插件来监视队列的长度、消息吞吐量和节点状态。

  6. 硬件和基础设施的冗余

    在硬件层面,确保服务器、存储和网络连接的冗余可以减少单点故障的风险。使用如RAID或SAN等存储方案可以提高存储系统的可靠性。

  7. 负载均衡(Load Balancing)

    使用负载均衡器可以分散到集群的连接和流量。这不仅增加了系统的吞吐量,也避免了单个节点的过载。

  8. 备份和恢复(Backup and Recovery)

    定期备份RabbitMQ的状态,包括消息队列的配置和消息数据。在发生灾难性故障时,可以用备份快速恢复服务。

  9. 升级和补丁管理

    定期应用RabbitMQ和操作系统的更新和补丁,可以减少因为已知安全问题或软件缺陷导致的服务中断。

  10. 故障切换(Failover)策略

    为系统设计故障切换策略,以便在节点或组件失败时能够快速恢复服务。

要实现这些策略,可能需要对RabbitMQ的高级特性有深入的了解,并且在系统设计和运维阶段做出相应的规划和实施。此外,持续的性能测试和优化也是确保RabbitMQ高可用的重要环节。

相关推荐
bing_1587 分钟前
Spring Boot 中ConditionalOnClass、ConditionalOnMissingBean 注解详解
java·spring boot·后端
ergdfhgerty8 分钟前
斐讯N1部署Armbian与CasaOS实现远程存储管理
java·docker
勤奋的知更鸟22 分钟前
Java性能测试工具列举
java·开发语言·测试工具
三目君25 分钟前
SpringMVC异步处理Servlet
java·spring·servlet·tomcat·mvc
用户05956611920925 分钟前
Java 基础篇必背综合知识点总结包含新技术应用及实操指南
java·后端
fie888926 分钟前
Spring MVC扩展与SSM框架整合
java·spring·mvc
不太可爱的叶某人33 分钟前
【学习笔记】深入理解Java虚拟机学习笔记——第3章 垃圾收集器与内存分配策略
java·笔记·学习
YuTaoShao34 分钟前
Java八股文——JVM「类加载篇」
java·开发语言·jvm
爱玩电脑的L1 小时前
javaee初阶-多线程
java·开发语言·jvm
小王不会写code1 小时前
Hadoop 2.7.7 单机伪分布式安装与配置教程(JDK 8)
java·hadoop·分布式