系统设计高频率元件之讯息伫列 (Message Queue)
2024年6月21日
先前在《系统设计高频率元件之 Redis》一文我们谈了 Redis,今天要进一步来谈另一个系统设计中,高频率会用到的元件 — 讯息伫列 (Message Queue)。
伫列与讯息伫列
相信多数人对于伫列 (Queue) 这个资料结构都不陌生,伫列是一个先进先出 (First-In, First-Out,FIFO) 原则的资料结构。就像在排队买东西一样,最先进入队伍的那个人会最先被服务。事实上,在英文当中 queue 这个词就是平常在排队时会用的词。
假如你对伫列还不熟,非常推荐《An interactive study of queueing strategies》这个网路教材,可以让你快速掌握伫列这个资料结构,推荐读完后再往下看这期的内容。
如果你读完上面那篇《An interactive study of queueing strategies》,让我们回到讯息伫列这个主题上。如其名所述,讯息伫列是专门用来传递讯息的伫列。一般来说,讯息伫列会用来在系统中不同元件之间传递讯息,例如在不同的应用或服务之间传递讯息。如同上面提到的伫列,讯息伫列的运作方式,也是先进的讯息先处理。
具体来说,会是像下面这样
1. 生产者发送讯息到伫列:
+------------+ +--------------+
| 生产者 |--Message 1->| 讯息伫列 |
| |--Message 2->| [Message 1] |
| |--Message 3->| [Message 2] |
+------------+ | [Message 3] |
+--------------+
2. 消费者从伫列取出最先放入的讯息 1 进行处理:
+--------------+ +--------------+
| 讯息伫列 |--Message 1->| 消费者 |
| [Message 1] | | |
| [Message 2] | +--------------+
| [Message 3] |
+--------------+
3. 消费者继续处理讯息 2:
+--------------+ +--------------+
| 讯息伫列 |--Message 2->| 消费者 |
| [Message 2] | | |
| [Message 3] | +--------------+
+--------------+
4. 最后处理讯息 3:
+--------------+ +--------------+
| 讯息伫列 |--Message 3->| 消费者 |
| [Message 3] | | |
+--------------+ +--------------+
上面这张图可以看到,讯息伫列作为传递讯息的中介角色,会把从生产者 (producer) 拿到的讯息,依照先进先出的顺序,传给消费者 (consumer)。
为什么要用讯息伫列?
上面这样讲完,你大概能懂讯息伫列在做什么。只是为什么要在系统中用这个元件呢? 毕竟系统中每多一个元件,复杂性就会提升,没有特别好处的话,没道理要用。 对此,让我们透过实际的例子来了解,讯息伫列的不同用途。
协助解耦
在系统中最常用到讯息伫列的用途之一,是当有多个消费者,需要收到同样的讯息。举例来说,今天在一个电商平台底下有订单系统,在一笔新的订单下来后,需要同步让不同的微服务获得该笔订单的资讯。
这时订单系统把订单资讯发给讯息伫列,然后其他系统,可以从该讯息伫列获得该笔订单资讯。在这个设计下,订单系统跟其他系统彼此是解耦合的,因为订单系统只需用确保把讯息发到讯息伫列,后面有哪些系统要接收讯息,订单系统不用担心。而后面要接的其他系统,想要接入或移除,都不用动到订单系统的逻辑,这让系统的灵活性与可扩展性都有所提升。
+-----------+ +-------------+
| 订单 | | 讯息 |
| 系统 |----> | 伫列 |
+-----------+ +-------------+
/ / | \\
/ / | \\
+--------+ +-------+ +-------+ +-------+
| 仓储 | | 物流 | | 客服 | | 分析 |
| 系统 | | 系统 | | 系统 | | 系统 |
+--------+ +-------+ +-------+ +-------+
做为缓冲提高故障容忍度
除了解耦外,另一个讯息伫列带来的优点是能够做为缓冲。以目前常见的电子信系统来说,一个发信人发出电子信后,会有数百到数万的收件人要收。那么假如其中有收件人的服务出问题,假如是直接传的方式,就会让讯息丢失。
因此,当发信人发出新一期的电子信后,不会直接传给收件人,而是会先进到讯息伫列。讯息伫列在这边可以做为一个缓冲区,如果收件人的电子信箱服务出问题,信件还停留在伫列中,直到问题被解决后再发送,这样做将能提高系统的故障容忍度。
+------------+ +---------------+ +------------+
| 发信人 | ----> | 讯息伫列 | ----> | 收件人 |
+------------+ +---------------+ +------------+
作为缓冲区,有时也能够解决运算过度繁重的问题。举例来说,现在的生成式 AI 应用,背后都需要有繁重的运算任务,例如影音生成、文字转语音等。如果直接把这些任务放在伺服器处理,伺服器可能应接不暇。
这时有讯息伫列作为缓冲区,可以先把任务丢到伫列中,然后有专门处理的 Worker 来处理 (备注:Worker 是指专门处理特定任务的伺服器)。如果目前有的 Worker 都在处理其他任务,也不担心。可以等有 Worker 有闲置产能时,在去伫列中拿任务来处理。而待处理的任务,会一直在伫列中,不担心会丢失。
1. 使用者需要某个运算繁重的任务 (例如要文字转语音)
+---------+ +------------+
| 使用者 | ----> | 伺服器 |
+---------+ +------------+
2. Web 伺服器将请求放入讯息伫列:
+------------+ +--------------+
| 伺服器 | ----> | 讯息伫列 |
+------------+ +--------------+
3. 丢给对应的 Worker 处理 (例如文字转语音的 Worker)
+--------------+ +---------+
| 讯息伫列 | ----> | Worker |
+--------------+ +---------+
4. 处理完成后,通知用户或更新状态:
+---------+ +---------+
| Worker | ----> | 使用者 |
+---------+ +---------+
更多深入内容
以上我们介绍了讯息伫列的基本概念,以及为什么要用讯息伫列。在实际做系统设计时,还有一些深入概念推荐大家要知道,而我们在 E+ 的深度版本,会进一步谈到:
- Pub/Sub 设计模式
- 如何解决使用讯息伫列的不一致问题
- 哪些场景不适合使用讯息伫列
- 不同的讯息伫列工具,该选择哪一个
有兴趣的人,欢迎加入 E+ 成长计划,详细介绍可以 参考此页面。