1. 什么是消息队列(MQ)
1.1 队列
首先从队列说起,队列与栈相反,队列是先进先出,可以理解为处理的时候是从开头开始处理。
1.2 消息
是两个应用之间传递的数据,一般是一个byte数组,byte数组里面可能会包含类似于http中的header部分和body部分。所以从这点看,消息队列的消息和http协议还是有一些相类似的地方。
1.3 消息队列
而消息队列,即 message queue,简称 MQ,是在两个应用之间传递消息的队列,在消息队列非空的情况下,消息队列中包含了一系列待处理的消息。
消息队列提供异步通信协议,该协议是一种将消息放入消息队列并且不需要立即响应来继续处理的系统。消息队列将一次RPC调用分解为两次RPC调用,消息队列提供了一种异步通讯机制,发布者将消息发送到队列,发送成功了就行,并不关心这个消息的最终处理情况如何,消息的订阅者再从消息队列中获取这个消息进行处理。所以整个过程中,消息的发布者并不关心时谁消费了他发的消息,订阅者也不关心这个消息时谁发的。发布者和订阅者只与消息队列通讯,并不关心对方,这样可以做到发布者和订阅者的耦合。
2. 为什么使用消息队列
-
在很多业务场景中,有些流程对即时性的要求是不高的。比如在电商下单后,可能会有短信通知你商品商品在打包,即将发货。显然这种短信对我们来说没什么重要意义。那么可以将这种对即时性要求不高的逻辑处理放入队列。比如下单完成后发送短信功能。
-
另外一种场景是某个操作非常的耗时,比如用户可能需要等待5秒以上才能完成,因为处理时间过长,很有可能会出现超时的问题。等待时间过长也会带来不良的用户体验。所以在这种场景,可以将耗时的操作用消息队列处理。比如12306的买票功能,下单后不会立刻提醒买票成功,而是会显示处理中,大概多长时间会好。用户可以用等待的时间去做其他事情,而不是一直等待结果。时间差不多了回来刷新一下界面看看有没有购票成功。
-
当客户端的处理速度和服务器的处理速度相差较多时,比如客户端处理很快,不断向服务器发送消息,服务器无法立刻处理太多请求。那么可以用消息队列做一个“漏斗”,客户端将消息发给消息队列,消息存放到消息队列之后,服务器有能力处理时再从消息队列中拿消息出来处理,防止服务器过载。
3. 常见模式
3.1 点对点模式(point to point)
如下图:
point-to-point
生产者将消息发送到queue,消费者从queue取出消息并消费。
一个生产者发的消息只能被一个消费者消费,生产者是一点,消费者是一点,即所谓的点对点模式。另外对于queue,一旦消息被消费者消费,则会将队列中的这条消息删除,则下次其他消费者再获取消息时不会再获取到被消费的消息,防止重复消费。
注意从图上可以看出,点对点模式可以有多个发布者,多个订阅者,也不一定是Producer1的消息每次都会被Consumer1消费,只要保证只被一个消费者消费即可。
3.2 发布/订阅模式(pub/sub)
如下图
pub/sub
这种是不同于“单播”的点对点格式的“广播”方式。pub与sub即publish与subscribe。发布者发布一个消息后,可以被多个消费者消费。它们之间的对应关系是通过一个类似于广播里的频道的概念所关联。只要发布者发布了某一个频道(这里指topic)的消息,所有订阅了该频道的订阅者都可以收到这个消息。所以一个消息被多个订阅者消费。
分组订阅
实际部署时经常会用到订阅组,一个组里面有多个消费者,每个消费者订阅不同的topic。然后多个组部署后,每个组都可以接受到消息,组内再区分哪个消费者消费。可以看成是一个topic下有多个Queue,每个Queue是点对点的方式,Queue之间是发布订阅方式。如下图所示。
image.png
4. 组成
4.1 publisher
发布者,负责产生和发送消息到 broker
4.2 broker
消息队列服务端,实际上消息队列是可以没有broker的,比如Akka(actor模型)、ZeroMQ等,其实都是基于消息的系统设计范式,并没有broker。broker一般包含一个或者多个 queue。broker做的内容包括接受publisher发来的消息,用持久化或者非持久化的方式存储消息,然后consumer获得消息后如果消费成功,broker收到消费成功的消息将对应消息从队列中删除。如果消费失败,则会重试n次,如果还失败则将该消息加入死信队列。
4.3 consumer
消息消费者,负责从 broker 中获取消息,并进行相应处理
补充:关于消费者如何从broker中获取消息,有两种模式,分别叫 pull 和 push
4.3.1 消费者通过pull获得消息
消费者觉得自己有能力消费消息时,向消息队列发出申请,申请消息。市面上有很多经典的也比较成熟的pull模型的消息队列,如Kafka、MetaQ等。主动权在消费方。
4.3.1 消息队列push消息给消费者
消息队列通过一定的机制将消息发给他认为合适的消费者,主动者在消息队列。常见问题是如果消费者的速度比发送者的速度慢很多,势必造成消息在broker的堆积。
5. 消息队列的优点
1.通过异步处理提高系统性能(削峰、减少响应所需时间);
2.屏蔽异构平台的细节:发送方、接收方系统之间不需要了解双方,只需认识消息。
3.异步:消息堆积能力;发送方接收方不需同时在线,发送方接收方不需同时扩容(削峰)。
4.解耦:防止引入过多的API给系统的稳定性带来风险;调用方使用不当会给被调用方系统造成压力,被调用方处理不当会降低调用方系统的响应能力。
5.复用:一次发送多次消费。
6.可靠:一次保证消息的传递。如果发送消息时接收者不可用,消息队列会保留消息,直到成功地传递它。
7.提供路由:发送者无需与接收者建立连接,双方通过消息队列保证消息能够从发送者路由到接收者,甚至对于本来网络不易互通的两个服务,也可以提供消息路由。
6. 消息队列的缺点
- 系统可用性降低: 考虑消息丢失或者说消息队列挂掉等情况。
- 系统复杂性提高: 加入消息队列之后,需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
- 一致性问题:消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费,这样就会导致数据不一致的情况了。需要考虑实现最终一致性(最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。)
5. 消费顺序
在这点上,消息队列的基本要求是需要保证顺序是FIFO的,消息不能被重复消费。
7. 常见的消息队列
- ActiveMQ
- RabbitMQ:推荐
- RocketMQ:有可能消息重复消费,适合日志系统
网友评论