RabbitMQ面试题解析
1.消息队列的作用与使用场景?
异步:批量数据异步处理。例:批量上传文件,比如代发代扣文件
削峰:高负载任务负载均衡。例:电商秒杀抢购
解耦:串行任务并行化。例:退货流程解耦。
广播:基于Pub/Sub实现一对多通信
2.多个消费者监听一个队列时,消息如何分发?
1.Round-Robin(轮询)
默认的策略,消费者轮流,平均地收到消息。
2.Fair dispatch(公平分发)
如果要实现根据消费者的处理能力来分发消息,给空闲的消费者发送更多消息,可以用basicQos(int prefetch_count)来设置。prefetch_count的含义:当消费者有多少条消息没有响应ACK时,不再给这个消费者发送消息。
3.无法被路由的消息,去了哪里?
如果没有任何设置,无法路由的消息会被直接丢弃。
无法路由的情况:Routing key不正确
解决方案:
1.使用mandatory = true配合ReturnListener,实现消息回发
2.声明交换机时,指定备份交换机。
4.消息在什么时候会变成Dead Letter(死信)?
1.消息被拒绝并且没有设置重新入队:(NACK || Reject)&&requeue == false
2.消息过期(消息或者队列的TTL设置)
3.消息堆积,并且队列达到最大长度,先入队的消息会变成DL。
可以在声明队列的时候,指定一个Dead Letter Exchange,来实现Dead Letter的转发。
5.RabbitMQ如何实现延时队列?
利用TTL(队列的消息存活时间或消息的存活时间),加上死信交换机。
当然还有一种方式就是先保存到数据库,用调度器扫描发送(时间不够精准)
6.如何保证消息的可靠性传递?
1.确保投递到服务端Broker
有两种解决方案,第一种是Transaction(事务)模式,第二种Confirm(确认)模式。 在通过channel.txSelect方法开启事务之后,我们便可以发布消息给RabbitMQ了,如果事务提交成功,则消息一定 到达了RabbitMQ中,如果在事务提交执行之前由于RabbitMQ异常崩溃或者其他原因抛出异常,这个时候我们便 可以将其捕获,进而通过执行channel.txRollback方法来实现事务回滚。使用事务机制的话会“吸干”RabbitMQ的性 能,一般不建议使用。
生产者通过调用channel.confirmSelect方法(即Confirm.Select命令)将信道设置为confirm模式。一旦消息被投 递到所有匹配的队列之后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID),这就使得 生产者知晓消息已经正确到达了目的地了。
2.保证正确的路由
使用mandatory参数和ReturnListener,可以实现消息无法路由的时候返回给生产者。
另一种方式就是使用备份交换机(alternate-exchange),无法路由的消息会发送到这个交换机上。
3.消息的持久化存储
4.消费者应答ACK
为了保证消息从队列可靠地达到消费者,RabbitMQ提供了消息确认机制(message acknowledgement)。消费 者在订阅队列时,可以指定autoAck参数,当autoAck等于false时,RabbitMQ会等待消费者显式地回复确认信号 后才从队列中移去消息。
如果消息消费失败,也可以调用Basic.Reject或者Basic.Nack来拒绝当前消息而不是确认。如果requeue参数设置为 true,可以把这条消息重新存入队列,以便发给下一个消费者(当然,只有一个消费者的时候,这种方式可能会出 现无限循环重复消费的情况,可以投递到新的队列中,或者只打印异常日志)
5.消费者回调
消费者处理消息以后,可以再发送一条消息给生产者,或者调用生产者的API,告知消息处理完毕。
6.补偿机制
对于一定时间没有得到响应的消息,可以设置一个定时重发的机制,但要控制次数,比如最多重发3次,否则会造 成消息堆积。
7.消息幂等性是什么?
Broker本身没有消息重复过滤的机制
1.生产者方面,可以对每条消息生成一个msgId,以此控制消息重复投递。
//消息属性 AMQP.BasicProperties properties = new AMQP.BasicProperties.builder() .messageId(String.valueOf(UUID.randomUUID())) .build(); //发送消息 channel.basicPublish("",QUEUE_NAME,properties,msg.getBytes());
2.消费者方面,消息体(比如json报文)中必须携带一个业务ID,比如银行得到交易流水号,消费者可以根据业务ID去重,避免重复消费。
8.如何在服务端和消费端做限流?
网关/接入层:其他限流方式。
服务端(Broker):配置文件中内存和磁盘的控制;(队列长度无法实现限流)。
消费端:prefetch_count.
9 如何保证消息的顺序性?
比如新增门店,绑定产品,激活门店这种对消息顺序要求严格的场景。
一个队列只有一个消费者的情况下才能保证顺序。
否则只能通过全局ID来实现。
1.每条消息有一个msgId,关联的消息拥有同一个parentMsgId。
2.可以在消费端实现前一条消息未消费,不处理下一条消息;也可以在生产端实现前一条消息未处理完毕,不发布下一条消息。
10.SpringBoot中,bean还没有初始化好,消费者就开始监听取消息,导致空指针异常,怎么让消费者在容器启动完毕后才开始监听?
RabbitMQ中有一个auto_startup参数,可以控制是否在容器启动时就监听启动
全局参数:
spring.rabbitmq.listener.auto-startup=true ##默认是true
自定义容器,容器可以应用到消费者:
// 默认是true factory.setAutoStartup(true);
消费者单独设置
@RabbitListener( queues = "${com.gupaoedu.thirdqueue}" ,autoStartup = "false")