分布式消息通讯-RabbitMQ秒杀面试题

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")
原文地址:https://www.cnblogs.com/gougou1981/p/12860059.html