系统设计高频率元件之讯息伫列 (Message Queue)

2024年6月21日

💎 加入 E+ 成長計畫 與超過 400+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

先前在《系统设计高频率元件之 Redis》一文我们谈了 Redis,今天要进一步来谈另一个系统设计中,高频率会用到的元件 — 讯息伫列 (Message Queue)。

伫列与讯息伫列

相信多数人对于伫列 (Queue) 这个资料结构都不陌生,伫列是一个先进先出 (First-In, First-Out,FIFO) 原则的资料结构。就像在排队买东西一样,最先进入队伍的那个人会最先被服务。事实上,在英文当中 queue 这个词就是平常在排队时会用的词。

假如你对伫列还不熟,非常推荐《An interactive study of queueing strategies》这个网路教材,可以让你快速掌握伫列这个资料结构,推荐读完后再往下看这期的内容。

《An interactive study of queueing strategies》
圖片來源:《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+ 成长计划,详细介绍可以 参考此页面

🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們