什么是消息队列
消息队列是一种 异步通信机制 ,用于在 分布式系统 中解耦发送方和接收方之间的通信。它通过在消息生产者和消费者之间引入一个 中间缓冲区 ,将消息存储在 broker 中,然后由消费者从 broker 中读取和处理消息。
消息队列中的核心概念
- 生产者(Producer) :负责向消息队列中发送消息的应用程序或服务。生产者将消息发送到指定的队列或主题(Topic),供消费者消费。
- 消费者(Consumer) :从消息队列中读取和处理消息的应用程序或服务。消费者根据业务逻辑处理收到的消息,并可以向消息队列发送确认。
- Broker :消息队列的核心组件,负责接收、存储和分发消息。
- 队列(Queue) :存储消息的容器,消息按照先进先出(FIFO)的顺序在队列中存储。队列中的每条消息通常只能被一个消费者消费一次。
- 主题(Topic) :用于在发布/订阅模型中,消息生产者将消息发布到一个主题,多个订阅该主题的消费者可以接收到相同的消息。
消息队列的模型
队列模型(点对点模型)
在队列模型中,消息从生产者发送到队列,并且每条消息只能被一个消费者消费一次。消费之后,消息在队列中被删除。适用于任务处理类场景,如一个任务只需要一个处理者执行。
存在问题:生产者向队列中投放消息,但是队列会有多个消费者,多个消费者之间存在竞争关系,即 每条消息只能被一个消费者消费
会造成数据冗余的问题
发布-订阅模型
在发布/订阅模型中,生产者将消息发布到某个主题(Topic),所有订阅了该主题的消费者都会接收到该消息。每个订阅者都会接收到相同的消息,适用于广播通知、实时推送等场景。可以将发布-订阅模型理解为将消费者都加入了一个群聊中,生产者发一条消息,加入了这个群聊的消费者都能收到这条消息。
消息队列的作用
服务解耦
生产者和消费者无需同时在线,生产者可以发送消息后立即返回,而消费者在合适的时机处理消息。消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合 ,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。 对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现可扩展性设计。
削峰填谷和流量限制
在高并发场景下,消息队列可以暂存大量请求,平滑高峰流量,避免系统过载。在项目开发中,后端服务相对而言都是比较「弱」的,因为业务较重,处理时间较长。消息队列作为中间件可以起到缓冲的作用。网关的请求先放入消息队列中,后端服务尽自己最大能力去消息队列中消费请求。超时的请求可以直接返回错误。
异步处理
将用户请求中包含的耗时操作,通过消息队列实现异步处理。
生产者将对应的消息发送到消息队列之后就立即返回结果,减少响应时间,提高用户体验。随后,消费者再对消息进行消费。请求数据在后续的业务校验、写数据库等操作中可能失败。因此,使用消息队列进行异步处理之后,需要适当修改业务流程进行配合
推模式和拉模式
推模式和拉模式指的是消费者和Broker之间的交互过程
-
推模式(Push) :消息队列将消息主动推送给消费者,适合实时性要求高、消费者能够及时处理消息的场景。
-
优点 :实时性好,消息可立即送达消费者。
-
缺点 :难以控制消费速度,容易导致消费者过载,尤其是在高并发时。
-
-
拉模式(Pull) :消费者主动从消息队列中拉取消息,适合消费能力有限、需要根据自身处理能力调控速率的场景。
-
优点 :消费者可以根据自身负载决定拉取频率,避免过载;更适合批量处理。
-
缺点 :可能会导致消息延迟,实时性不如推模式,尤其是拉取频率较低时。
-
选择策略
个人认为相较于推模式,拉模式更加适应当下的需求。因为现在的消息队列都有持久化消息的需求,也就是说本身它就有个存储功能,它的使命就是接受消息,保存好消息使得消费者可以消费消息即可,身为 Broker 不应该有依赖于消费者的倾向。
在Kafka中的实现
Kafka通过长轮询来实现拉模式。具体的做法都是通过消费者去 Broker 拉取消息时,当有消息的情况下 Broker 会直接返回消息,如果没有消息都会采取延迟处理的策略,即保持连接,暂时 hold 主请求,然后在对应队列或者分区有新消息到来的时候都会提醒消息来了,通过之前 hold 主的请求及时返回消息,保证消息的及时性。
在Kafka中设置了超时参数,消费者去 Broker 拉消息,定义了一个超时时间,也就是说消费者去请求消息,如果有的话马上返回消息,如果没有的话消费者等着直到超时,然后再次发起拉消息请求。并且 Broker 也得配合,如果消费者请求过来,有消息马上返回,没有消息那就建立一个延迟操作,等条件满足了再返回。