消息队列
什么是消息队列
消息队列(Message Queue)是一种进程间通信或同一进程的不同线程间的通信方式。
比如Go里面的channel就是一种典型的消息队列, 用于不同的goroutine进行通信
func main() {
ch := make(chan, 5)
// 生产消息(子Gorouine)
go func() {
for i:=0;i<10;i++ {
ch <- i
}
}()
// 消费消息(主Goroutine)
for v := range ch {
fmt.Println(v)
}
}
这种语言内置的消息队列数据结构, 只能用于进程内通信, 一旦跨进程通信就无法使用
消息队列的作用
以买快递为例
- 异步,将同步的消息变为异步,快递小哥无需等待购买者来取快递
- 解耦:将生产者和消费者解耦。比如快递站对于购买者和快递小哥
- 消峰:由于是异步,解耦的,高并发请求到来时不直接发送给服务,而是发给MQ,让服务决定什么时候接收消息,提供服务,这样就缓解了服务的压力
消息服务器
如果我们把消息队列做成一个服务, 让它监听一个端口接收消息,然后把消息转发给其他进程 是不是就实现了 进程间的 基于消息队列的通信。
像上面说的这种 用于接收和转发消息的 服务,往往被叫做: 消息服务器/消息中间件
使用场景
消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然, 基于这个衍生出来很多使用场景:
-
异步处理:例如短信通知、终端状态推送、App推送、用户注册等
有些业务不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。在需要的时候再去处理它们。 -
数据同步:业务数据推送同步
比如阿里云有个事件通知服务, 它会把消息放到你指定的队列中, 用于事件通知 -
重试补偿:retry机制,失败后放入队列等待下次尝试,而非立即重试 -
流量消峰:秒杀场景下的下单处理
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量无法提取预知;如果以为了能处理这类瞬间峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃 -
数据流处理:日志服务、监控上报
分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量采集汇总,然后进行大数据分析是当前互联网的必备技术,通过消息队列完成此类数据收集是最好的选择 -
系统解耦:通讯上下行、终端异常监控、分布式事件中心
降低工程间的强依赖程度,针对异构系统进行适配。在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。通过消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口,当应用发生变化时,可以独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束
核心概念
Broker(消息服务器)Broker的概念来自于Apache ActiveMQ,通俗的讲就是MQ的服务器。Producer(生产者)业务的发起方,负责生产消息传输给brokerConsumer/Subscriber(消费者/订阅者)业务的处理方,负责从broker获取消息并进行业务逻辑处理Topic(主题)发布订阅模式下的消息统一汇集地,不同生产者向topic发送消息,由MQ服务器分发到不同的消费者,实现消息的广播Message(消息体)根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输
消息模型
点对点模型
最基本的队列模型, 可以将他理解为Go里面的channel, 只是Sender往Queue发送消息, Receiver从Queue中接收消息
需要注意的是一旦被消费,消息就不再在消息队列中
优点:消费者拉取消息的频率可以由自己控制。
缺点:消息队列是否有消息需要消费,消费者无法感知,需要额外的线程去监控。
发布订阅模型
当你的消息需要多个消费者都能收到时(广播机制), 很显然点对点模式无法满足, 此时就会用到发布订阅模型, 这是最常用到的模型: 多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者
由于采用类似于广播的模式, 它有如下特点:
- 每个消息可以有多个消费者:和点对点方式不同,发布消息可以被所有订阅者消费
- 发布者和订阅者之间有时间上的依赖性。
- 针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息。
- 为了消费消息,订阅者必须保持运行的状态
优点:消费者被动接收推送,所以无需感知消息队列是否有待消费的消息
缺点:消费者由于机器性能不同,处理消息的能力也会不同,但mq无法感知消费者消费的速度!假设三个消费者处理速度分别是8M/s、5M/s、2M/s,如果队列推送的速度为5M/s,则consumer3无法承受!如果队列推送的速度为2M/s,则consumer1、consumer2会出现资源的极大浪费!