消息队列常见面试题

1.为什么要用消息队列

解耦、异步、削峰

  • A系统调用B系统、C系统,传统的调用是直接调用,但是当B系统说我不需要你提供数据了,这时候A需要改代码,C系统说我不需要某个字段了,这时候A也要改代码,如果又多了一个D系统,A又要写代码。为了实现解耦,引入消息队列,A将产生的数据丢到消息队列中,哪个系统需要 哪个系统就去取;
  • A系统调用B系统,B系统由于某个需要调用第三方接口超时,导致A系统响应速度慢,而B系统的好坏又不会影响业务逻辑,所以可以改为A异步调用B,A将消息丢到消息队列中,B系统订阅消息,实现A的快速响应;
  • 当大量流量请求到系统A时,由于数据库的处理能力有限,造成数据库连接异常。使用消息队列,大量请求先丢到消息队列中,系统A使用按批拉数据的方式,批量处理数据,生产中,高峰期短暂的消息积压是允许的。
2.使用消息队列有什么缺点
  • 系统复杂性增加:加了消息队列,需要保证消息不会重复消费,需要保证消息的可靠性,需要保证消息队列的高可用
  • 系统的可用性降低:如果消息队列挂了,那么系统也会受到影响
3.为什么选用RocketMQ;RocketMQ和ActiveMQ的区别

RocketMQ模型简单、接口易用,在阿里大规模使用,社区活跃,单机吞吐量10万级,可用性非常高,消息理论上不会丢失;

  • ActiveMQ严格遵循JMS规范,可持久化到内存、文件、数据库,可用性高主要是主从,多语言支持,消失丢失率低;
  • RocketMQ持久化到磁盘文件,可用性非常高,支持分布式,只支持Java,消息理论上不会丢失;

4.RocketMQ是怎么保证系统高可用的?

  • 多Master部署,防止单点故障;
  • 主从结构,消息冗余,防止消息丢失;
5.MQ能否保证消息必达,即消息的可靠性?

为了降低消息丢失的概率,MQ需要进行超时和重传

(1) MQ-client-sender 发送消息给MQ-server

(2) MQ-server接收到消息后,发送 ACK消息给发送方

(3) MQ-client-sender 接收到 ACK消息后,则 消息已经投递成功

如果上述 2 消息丢失或者超时,MQ-client-sender 内的 timer 会重发消息,直到收到 ACK消息,如果重试N次后还未收到,则回调发送失败。需要注意的是,这个过程中 MQ-server 可能会收到同一条消息的多次重发。

对每条消息,MQ系统内部必须生成一个inner-msg-id,作为去重和幂等的依据,这个内部消息ID的特性是:

  • 全局唯一
  • MQ生成,具备业务无关性,对消息发送方和消息接收方屏蔽

(4) MQ-server 将消息发送给 MQ-client-receiver

(5) MQ-client-receiver 得到消息处理业务逻辑

(6) MQ-client-receiver 回复 ACK消息给 MQ-server

(7) MQ-server收到 ACK消息,将已消费的消息删除

如果上述 6 消息丢失或者超时,MQ-server 内的 timer 会重发消息,直到 MQ-server 收到ACK消息 并且 将已消费的消息删除,这个过程也可能会重发多次,MQ-client-receiver 也可能会收到同一条消息的多次重发。

需要强调的是,MQ-client-receiver 回ACK给 MQ-server,是消息消费业务方的主动调用行为,不能由 MQ-client-sender 自动发起,因为MQ系统不知道消费方什么时候真正消费成功。

为了保证业务幂等性,业务消息体中,必须有一个biz-id,作为去重和幂等的依据,这个业务ID的特性是:

  • 对于同一个业务场景,全局唯一
  • 由业务消息发送方生成,业务相关,对MQ透明
  • 由业务消息消费方负责判重,以保证幂等

最常见的业务ID有:支付ID,订单ID,帖子ID等。

欢迎关注我的公众号~ 搜索公众号: 码咖 或者 扫描下方二维码:

### 关于消息队列 MQ 的常见面试题及其解答 #### 1. 生产者丢失消息的原因及解决方案 生产者可能会因为网络传输中的丢包或者消息队列服务端发生异常未能成功接收消息而导致消息丢失。为了防止这种情况的发生,主流的消息队列产品通常提供了确认机制或事务支持功能来保障消息能够被可靠地传递至消息队列服务器。例如,在 RabbitMQ 中存在两种主要方式用于确保消息不会丢失——事务模式和 Confirm 模式[^1]。 #### 2. 如何保证消息的顺序性? 对于某些业务场景而言,保持消息按特定次序处理是非常重要的需求之一。RabbitMQ 可通过配置单线程消费者以及设置 `prefetch_count=1` 来实现这一目标。这意味着每次只允许一个未完成的任务分配给某个具体的消费者实例直到前一任务结束为止从而维持全局有序性[^3]。 #### 3. 部署复杂度与运维挑战 随着企业规模扩大和技术架构日益复杂化, 对应地也提高了对开发人员特别是 DevOps 工程师们的技术水平要求。他们不仅需要熟悉各种开源工具链如 Docker/Kubernetes 等容器编排平台的知识点外还需掌握诸如 Kafka/Zookeeper 或 Pulsar/BookKeeper 类型分布式流计算框架的工作原理以便更好地管理和维护整个系统生命周期内的稳定性表现良好性能指标达成预期效果满足实际应用场景下的多样化需求特点[^2]. #### 4. 数据库写入与缓存更新的一致性问题 当应用程序既涉及到了数据库记录修改又包含了关联对象存储(比如Redis)状态刷新时,则很容易遇到因时间差造成两者间短暂不同步的现象。一种可行的办法就是在执行SQL语句之前先把对应变动通知推送到中间件上等待后续由专门负责监听该主题订阅者的回调函数再去主动触发相应的动作以此达到最终一致性的目的尽管在此过程中可能存在短暂时延使得部分查询请求返回陈旧版本的信息内容但是总体来说这种方法兼顾了吞吐量提升的同时又能较好控制风险范围不至于影响整体服务质量水准下降太多程度范围内可接受的程度之内[^4]。 ```python import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() def update_cache(message): # Simulate cache updating logic here. pass def on_message(ch, method, properties, body): message = json.loads(body.decode()) try: with database_transaction(): process_database_update(message) ch.basic_ack(delivery_tag=method.delivery_tag) update_cache(message) except Exception as e: logging.error(f"Failed to handle message {message}: {e}") ch.basic_nack(delivery_tag=method.delivery_tag) channel.basic_consume(queue='task_queue', on_message_callback=on_message) channel.start_consuming() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值