zoukankan      html  css  js  c++  java
  • 分布式消息通讯-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")
  • 相关阅读:
    DevExpress的GridControl的实时加载数据解决方案(取代分页)
    WinForm程序虚拟分页(实时加载数据)
    C#使用反射特性构建访问者模式
    WinApi学习笔记内存的复制,填充,输出等操作
    PL/SQL学习笔记程序包
    WinApi学习笔记创建进程
    PL/SQL学习笔记触发器
    WinApi学习笔记获取光驱中的信息
    WinApi学习笔记读写文件
    不通过配置文件启动WCF服务
  • 原文地址:https://www.cnblogs.com/gougou1981/p/12860059.html
Copyright © 2011-2022 走看看