articleList

12-玩转大厂里面的 RabbitMQ 核心技能面试

2025/03/17 posted in  RabbitMQ
Tags: 

如何理解 RabbitMQ 里面的虚拟主机

  • vhost 可以认为是一个虚拟的小型 rabbitmq 队列。内部均含有独立的,queue、exchange 和 binding 等。其拥有独立的权限系统,可以做到 vhost 范围的用户控制,更多用于做不同权限的隔离

  • 用于不同业务模块的逻辑隔离,一个 Virtual Host 里面可以有若干个 Exchange 和 Queue,同一个 VirtualHost 里面不能有相同名称的 Exchange 或 Queue

    • 默认是 /

      • /dev
      • /test
      • /pro

项目中为什么使用消息队列

  • 使用场景

    • 核心应用

      • 解耦:订单系统->物流系统
      • 异步:用户注册->发送邮件,初始化信息
      • 削峰:秒杀、日志处理
    • 跨平台 、多语言

    • 分布式事务、最终一致性

    • RPC 调用上下游对接,数据源变动->通知下属

  • 缺点

    • 系统可用性越低,外部依赖越多,依赖越多,出问题风险越大
    • 系统复杂性提高,需要考虑多种场景,比如消息重复消费,消息丢失
    • 需要更多的机器和人力,消息队列一般集群部署,而且需要运维和监控,例如 topic 申请等

项目怎么选择哪个消息队列产品

  • 业界主流的消息队列:Apache ActiveMQ、Kafka、RabbitMQ、RocketMQ

    • ActiveMQ

      • http://activemq.apache.org/
      • Apache 出品,历史悠久,支持多种语言的客户端和协议,支持多种语言 Java, .NET, C++ 等
      • 基于 JMS Provider 的实现
      • 缺点:吞吐量不高,多队列的时候性能下降,存在消息丢失的情况,比较少大规模使用
    • Kafka

      • http://kafka.apache.org/
      • 是由 Apache 软件基金会开发的一个开源流处理平台,由 Scala 和 Java 编写。Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理大规模的网站中的所有动作流数据(网页浏览,搜索和其他用户的行动),副本集机制,实现数据冗余,保障数据尽量不丢失;支持多个生产者和消费者
      • 类似 MQ,功能较为简单,主要支持简单的 MQ 功能
      • 缺点:不支持批量和广播消息,运维难度大,文档比较少, 需要掌握 Scala
    • RocketMQ

      • http://rocketmq.apache.org/
      • 阿里开源的一款的消息中间件,纯 Java 开发,具有高吞吐量、高可用性、适合大规模分布式系统应用的特点,性能强劲(零拷贝技术),支持海量堆积,支持指定次数和时间间隔的失败消息重发,支持 consumer 端 tag 过滤、延迟消息等,在阿里内部进行大规模使用,适合在电商,互联网金融等领域
      • 基于 JMS Provider 的实现
      • 缺点:社区相对不活跃,更新比较快,纯 java 支持
    • RabbitMQ

      • http://www.rabbitmq.com/
      • 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、C,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不错
      • 缺点:使用 Erlang 开发,阅读和修改源码难度大

项目中消息队列怎么避免重复消费,说下你知道哪几种方案

  • 任何消息队列产品不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重,kafka、rocketmq、rabbitmq 等都是一样的

  • 接口幂等性保障,消费端处理业务消息要保持幂等性。幂等性,通俗点说,就一个数据或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的

    • Redis

      • setNX(),做消息 id 去重 java 版本目前不支持设置过期时间

        //Redis中操作,判断是否已经操作过 TODO
        boolean flag = jedis.setNX(key);
        if (flag) {
            //消费
        } else {
            //忽略,重复消费
        }
        
      • Incr 原子操作:key 自增,大于 0 返回值大于 0 则说明消费过

        int num = jedis.incr(key);
        if (num == 1) {
            //消费
        } else {
            //忽略,重复消费
        }
        
      • 上述两个方式都可以,但是排重可以不考虑原子问题,数据量多需要设置过期时间,考虑原子问题

    • 数据库去重表

      • 某个字段使用 Message 的 key 做唯一索引
  • 核心还是业务场景,不一定每个消息消费都需要加上述的操作,比如下面的场景

    • 优惠券记录释放的 MQ 消息,即锁定的消息变成可用的,不管多少次都是一样的结果

      update coupon_record set state='NEW' where id =#{id} and state='LOCK';
      
    • 评论点赞计数,需要保证幂等性,因为一个消息就会导致数值发生变化

你知道 RabbitMQ 是如何保障消息可靠性投递的吗?

  • 什么是消息的可靠性投递

    • 保证消息百分百发送到消息队列中去

    • 详细

      • 保证 mq 节点成功接受消息
      • 消息发送端需要接受到 mq 服务端接受到消息的确认应答
      • 完善的消息补偿机制,发送失败的消息可以再感知并二次处理
  • RabbitMQ 消息投递路径

    • 生产者-->交换机->队列->消费者

    • 通过两个的点控制消息的可靠性投递

      • 生产者到交换机

        • 通过 confirmCallback
      • 交换机到队列

        • 通过 returnCallback
  • 建议

    • 开启消息确认机制以后,保证了消息的准确送达,但由于频繁的确认交互,rabbitmq 整体效率变低,吞吐量下降严重,不是非常重要的消息真心不建议用消息确认机制

你知道 RabbitMQ 是如何实现延迟队列的吗,说下使用场景

  • 什么是 rabbitmq 的死信队列

    • 没有被及时消费的消息存放的队列
  • 什么是 rabbitmq 的死信交换机

    • Dead Letter Exchange(死信交换机,缩写:DLX)当消息成为死信后,会被重新发送到另一个交换机,这个交换机就是 DLX 死信交换机

  • 消息有哪几种情况成为死信

    • 消费者拒收消息(basic.reject/ basic.nack),并且没有重新入队 requeue=false
    • 消息在队列中未被消费,且超过队列或者消息本身的过期时间 TTL(time-to-live)
    • 队列的消息长度达到极限
    • 结果:消息成为死信后,如果该队列绑定了死信交换机,则消息会被死信交换机重新路由到死信队列
  • 使用场景

    • 通过消息触发一些定时任务,比如在某一固定时间点向用户发送提醒消息
    • 用户登录之后 5 分钟给用户做分类推送、用户多少天未登录给用户做召回推送
    • 消息生产和消费有时间窗口要求:比如在天猫电商交易中超时未支付关闭订单的场景,在订单创建时会发送一条延时消息。这条消息将会在 30 分钟以后投递给消费者,消费者收到此消息后需要判断对应的订单是否已完成支付。如支付未完成,则关闭订单。如已完成支付则忽略
  • Cloud 微服务大课训练营里面的应用

    • 优惠券回收
    • 商品库存回收
    • 超时未支付-定时关单实现