面试-消息中间件

消息队列

作用:解耦、异步、削峰

缺点:可用性降低,复杂性增加

MQ常用协议

  • AMQP协议 AMQP即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制。优点:可靠、通用
  • MQTT协议 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)是IBM开发的一个即时通讯协议,有可能成为物联网的重要组成部分。该协议支持所有平台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器(比如通过Twitter让房屋联网)的通信协议。优点:格式简洁、占用带宽小、移动端通信、PUSH、嵌入式系统
  • STOMP协议 STOMP(Streaming Text Orientated Message Protocol)是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。优点:命令模式(非topic/queue模式)
  • XMPP协议 XMPP(可扩展消息处理现场协议,Extensible Messaging and Presence Protocol)是基于可扩展标记语言(XML)的协议,多用于即时消息(IM)以及在线现场探测。适用于服务器之间的准即时操作。核心是基于XML流传输,这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。优点:通用公开、兼容性强、可扩展、安全性高,但XML编码格式占用带宽大
  • 其他基于TCP/IP自定义的协议:有些特殊框架(如:redis、kafka、zeroMq等)根据自身需要未严格遵循MQ规范,而是基于TCP\IP自行封装了一套协议,通过网络socket接口进行传输,实现了MQ的功能。

MQ的通讯模式

  1. 点对点通讯:点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
  2. 多点广播:MQ适用于不同类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即能够将消息发送到多个目标站点(Destination List)。可以使用一条MQ指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ不仅提供了多点广播的功能,而且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ将消息的一个复制版本和该系统上接收者的名单发送到目标MQ系统。目标MQ系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽可能减少网络的传输量。
  3. 发布/订阅(Publish/Subscribe)模式:发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。在MQ家族产品中,MQ Event Broker是专门用于使用发布/订阅技术进行数据通讯的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。
  4. 集群(Cluster):为了简化点对点通讯模式中的系统配置,MQ提供 Cluster 的解决方案。集群类似于一个 域(Domain) ,集群内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用 Cluster 通道与其它成员通讯,从而大大简化了系统配置。此外,集群中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性

MQ幂等性怎么保证/怎么解决消息重复消费问题?

  1. 生成全局唯一的inner-msg-id:在消息发送端(上半场),MQ客户端应为每条消息生成一个全局唯一且业务无关的inner-msg-id。这个ID由MQ保证其唯一性,并且对业务透明。这样,即使在网络波动或超时导致的重发情况下,MQ服务器可以利用这个ID进行去重,确保相同的消息不会被处理多次。
  2. 业务层的去重处理:在消息的消费端(下半场),业务系统需要根据业务特点带入业务相关的biz-id。消费端通过这个biz-id来进行去重,以保证对于同一业务操作,即使收到了多次相同消息,也只会被处理一次。这要求业务系统具备去重逻辑,通常涉及到数据库的唯一约束或者分布式锁等机制来保证操作的幂等性。
  3. 利用乐观锁机制:借鉴数据库中乐观锁的思想,可以为涉及状态变更的业务数据添加版本号。每次更新前先检查版本号,只有在版本号匹配的情况下才执行更新操作,并更新版本号。这种方式可以防止并发下的重复操作影响数据一致性。
  4. 消息确认机制:MQ客户端在发送消息给MQ服务器后,等待服务器的确认(ACK)。如果确认丢失或超时,客户端会重发消息。为了避免因此导致的重复消息处理,MQ服务器内部需要有机制识别并丢弃重复的消息,例如利用上述提到的inner-msg-id进行去重。
  5. 消费端幂等设计:在消费端实现业务逻辑时,需要考虑到消息可能会被重复投递的情况。因此,消费端的业务处理逻辑应当设计成幂等的,即多次执行相同的操作不会对最终结果产生影响。
  6. 事务性消息:对于需要保证精确一次消费的场景,可以使用事务性消息。这意味着消息的发送和消费是原子性的,要么都成功,要么都失败。这种方式下,系统能更好地控制幂等性,但可能会牺牲一些性能。
  7. 限流和补偿机制:在高并发场景下,适当的限流策略可以避免系统因过载而产生错误的重复操作。同时,建立补偿机制可以在检测到异常操作时进行修正。

综上所述,保证MQ幂等性是一个系统性工程,既涉及到技术层面的改进,如ID生成、去重、乐观锁等,也需要业务逻辑层面的支持,如业务去重、幂等设计等。

MQ 中的消息过期失效了怎么办?

批量重导。就是大量积压的时候,直接将数据写到数据库,然后等过了高峰期以后将这批数据一点一点的查出来,然后重新灌入 MQ 里面去,把丢的数据给补回来。

在消息队列系统中,推送模式(Push)和拉取模式(Pull)是两种基本的消息传输机制。

推送模式:在MQ中也就是Broker收到消息后主动推送给Consumer的操作

拉取模式:在MQ中也就是客户端主动从服务器Broker端获取信息。

如果有100万消息堆积在MQ , 如何解决 ?

第一:提高消费者的消费能力 ,可以使用多线程消费任务

第二:增加更多消费者,提高消费速度

使用工作队列模式, 设置多个消费者消费消费同一个队列中的消息

第三:扩大队列容积,提高堆积上限

可以使用RabbitMQ惰性队列

实现生产者消费者问题时,可以采用三种方式:

\1. 使用 Object 的 wait/notify 的消息通知机制;

\2. 使用 Lock 的 Condition 的 await/signal 的消息通知机制;

\3. 使用 BlockingQueue 实现。

RabbitMQ 是一个在 AMQP(Advanced Message Queuing Protocol )基础上实现的,整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。生产者将消息发送给交换器,交换器和队列绑定 。

AMQP 协议的三层

  • Module Layer:协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。
  • Session Layer:中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。
  • TransportLayer:最底层,主要传输二进制数据流,提供帧的处理、信道服用、错误检测和数据表示等。

AMQP 模型的三大组件

  • 交换器 (Exchange):消息代理服务器中用于把消息路由到队列的组件。
  • 队列 (Queue):用来存储消息的数据结构,位于硬盘或内存中。
  • 绑定 (Binding):一套规则,告知交换器消息应该将消息投递给哪个队列。

Message:由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key、priority、delivery-mode(是否持久性存储)等。

Exchange(交换器)

消息并不是直接被投递到 Queue(消息队列) 中的,中间还必须经过 Exchange(交换器) 这一层,Exchange(交换器) 会把我们的消息分配到对应的 Queue(消息队列) 中。

Exchange(交换器) 用来接收生产者发送的消息并将这些消息路由给服务器中的队列中,如果路由不到,或许会返回给 Producer(生产者) ,或许会被直接丢弃掉 。

RabbitMQ 的 Exchange(交换器) 有 4 种类型,不同的类型对应着不同的路由策略direct(默认)fanouttopic, 和 headers,不同类型的 Exchange 转发消息的策略有所区别。

生产者将消息发给交换器的时候,一般会指定一个 RoutingKey(路由键),用来指定这个消息的路由规则,而这个 RoutingKey 需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效

RabbitMQ 中通过 Binding(绑定) 将 Exchange(交换器) 与 Queue(消息队列) 关联起来,在绑定的时候一般会指定一个 BindingKey(绑定建)

fanout:会把所有发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中,用于广播

direct:精确匹配,常用在处理有优先级的任务

topic:模糊匹配

headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配。
Rabbit5种工作模式

简单队列模式

工作队列模式:多个消费者消费一个队列

发布订阅模式:生产者将消息发送到交换器,然后交换器绑定到多个队列,监听该队列的所有消费者消费消息。

路由模式(direct):完全匹配

主题模式(topic):模糊匹配
RabbitMQ如何保证消息不丢失

开启生产者确认机制,确保生产者的消息能到达队列:(生产者丢消息)
• 只要消息成功发送到交换机之后,RabbitMQ就会发送一个ack给生产者(即使消息没有Queue接收,也会发送ack)。如果消息没有成功发送到交换机,就会发送一条nack消息,提示发送失败。

开启消费者确认机制:确保消费者成功消费(消费者丢消息)

开启持久化功能:(RabbitMQ自己丢东西)
• 当生产者发布一条消息到交换机上时,Rabbit会先把消息写入持久化日志,然后才向生产者发送ack响应。一旦从队列中消费了一条消息的话并且做了确认,RabbitMQ会在持久化日志中移除这条消息。在消费消息前,如果RabbitMQ重启的话,服务器会自动重建交换机和队列,加载持久化日志中的消息到相应的队列或者交换机上,保证消息不会丢失

RabbitMQ重复消费问题如何解决/幂等性怎么保证?

发送消息时让每个消息携带一个全局的唯一ID,在消费消息时先判断消息是否已经被消费过,保证消息消费逻辑的幂等性。具体消费过程为:

  1. 消费者获取到消息后先根据id去查询redis/db是否存在该消息
  2. 如果不存在,则正常消费,消费完毕后写入redis/db
  3. 如果存在,则证明消息被消费过,直接丢弃

如何保证 RabbitMQ 消息的顺序性?

以给 RabbitMQ 创建多个 queue,每个消费者固定消费一个 queue 的消息,生产者发送消息的时候,同一个订单号的消息发送到同一个 queue 中,由于同一个 queue 的消息是一定会保证有序的,那么同一个订单号的消息就只会被一个消费者顺序消费,从而保证了消息的顺序性。

什么是死信队列?消费失败的消息存放的队列。

如何导致的?

  • 消息被拒绝并且消息没有重新入队(requeue=false)
  • 消息超时未消费
  • 达到最大队列长度

当消息在一个队列中变成死信 消息之后,它能被重新被发送到一个死信交换机中,绑定 死信交换机 的队列就称之为死信队列。

什么是延迟队列?怎么实现的?

延迟队列指的是存储对应的延迟消息,消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。

使用 RabbitMQ 的死信交换机(Exchange)和消息的存活时间 TTL(消息存活时间)。

可以使用rabbitmq_delayed_message_exchange插件

RabbitMQ高可用机制

镜像模式集群,使用了3台机器。所有操作都是主节点完成,然后同步给镜像节点,如果主节点宕机后,镜像节点会替代成新的主节点。

我们可以采用仲裁队解决数据缺失,支持主从数据同步,主从同步基于Raft协议,强一致。

为什么选择RabbitMQ

  1. 消息顺序性:RabbitMQ对发送到队列的消息的顺序性提供了较强的保证。
  2. 消息的可靠性:RabbitMQ支持消息的可靠传递,并且支持事务。
  3. 重试逻辑和死信交换:RabbitMQ内置了对重试逻辑和死信交换的支持。
  4. 吞吐量和性能:RabbitMQ提供的高可靠性和顺序保证更加符合此类场景的需求。
  5. 系统的复杂性RabbitMQ作为一个消息代理中间件,可能更适合规模较小、需要高可靠性和顺序性保证的场景。

订单延迟关单怎么落地?

  • 下单事务提交后,发送一条延迟消息到 MQ(如 RabbitMQ、RocketMQ、Kafka+定时任务)。
  • 延迟时长 = 关单时长(如 30 min)。
  • 消息到期后被投递到真正的消费队列,由消费者检查订单状态,若仍是“待付款”则执行关单逻辑。
posted @ 2025-04-22 13:05  哒令,哒哒哒哒哒~令  阅读(26)  评论(0)    收藏  举报