天天看點

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

前言

消息隊列(Message Queue,下文簡稱MQ)是分布式網際網路架構中必不可少的核心元件,包括RocketMQ、Kafka、RabbitMQ等在業界廣泛使用的産品,在消息分發、異步解耦、削峰填谷、廣播通知等領域發揮着巨大的作用。

在MQ的使用過程中,線上對MQ元件進行遷移是一個非常普遍的需求,在如下的幾個場景中,都會涉及到MQ的線上遷移:

(1)規格更新。比如将3 Broker的Kafka叢集替換成6 Broker的Kafka叢集。

(2)更換另一種MQ産品。比如将RabbitMQ替換為性能和擴充性更強的RocketMQ。

(3)使用雲服務替換自建MQ叢集。比如将自建的RocketMQ叢集替換為雲上商業版RocketMQ服務。

在MQ遷移的過程中,存在3個非常重要的需求:

  • 操作簡單。
  • 風險可控。
  • 不影響業務系統的正常運作。

如何讓MQ的線上遷移同時滿足這3個重要的需求呢?本文将對幾種可行的方式進行深入探讨。

理論基礎

在涉及到MQ線上遷移的所有方案中,都存有一個很重要的原則:對于發往MQ的每一條消息,如果已經被它的消費者成功接收并得到處理,這條消息就不再具有業務含義。已經被成功接收并得到處理的消息,隻展現出統計方面的價值,并不需要随着MQ本身的遷移而進行資料遷移。

在資料庫遷移的場景中,新舊DB之間的資料遷移是非常重要的工作,這是因為DB中的資料是持久化的資料,需要伴随着資料庫的生命周期而長期存在。

而對于MQ而言,消息一旦被消息者接收并得到處理,就不再是持久化的資料,可以直接删除或歸檔。是以在MQ線上遷移的場景中,對已經處理過的消息,是沒有資料遷移必要的。這樣就将問題簡化為:如何在遷移的過程中確定每一條消息被成功接收并得到處理。

在系統維護期停業務遷移

我們先從一個最簡單方案開始體驗MQ線上遷移是如何進行的。對于不要求7*24小時連續運作的業務系統,可以利用系統維護期的時間視窗,通過停業務的方式來實作消息隊列的遷移。這種遷移方式因為不需要保證業務的連續性,操作起來就非常簡單。

(1)初始狀态

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

進入維護視窗期後,關閉生産者應用,這個時候不會再有的新的消息寫入MQ:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

在這個狀态下保持一段時間,當MQ上的所有消息都被消費者接收并成功處理後,就可以對消費者進行版本釋出,使其從新的MQ上接收消息:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

接下來再啟動生産者,使其指向新的MQ,整個操作就已經完成。當系統運作穩定後,可以對原MQ執行個體進行相關資料歸檔後直接下線:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

在停業務遷移方案中,最關鍵一步,在于如何在系統維護視窗期之内,確定原MQ上的所有消息被消費者接收并成功處理。因為生産者關閉之後,不會有新的消息寫入原MQ,隻要預留足夠長的時間,原MQ上堆積的消息一定會被消費者取走。在這個方案中,新MQ和原MQ也可以是不同的産品,比如從RabbitMQ遷移到RocketMQ也是可以支援的,因為生産者和消費者都經過了版本釋出的動作,隻需要在新版本中對API和收發邏輯進行修改就可以實作。

雙訂閱方案

在網際網路領域,能夠容忍維護期将業務暫停的系統越來越少了,7*24不間斷服務是行業的趨勢,上述的停業務遷移方案就不再适用了,如何在MQ遷移的過程中確定業務持續運作呢?

有一個非常棒的idea是讓消費者同時具有從原MQ和新MQ接收消息的能力,這樣不管生産方往哪一個MQ發送消息,都能夠確定消息得到及時的處理。這是一種不需要暫停業務的方案,我們來看一下具體的步驟。

首先對消費者進行改造,使其同時連上新老兩個MQ,具備同時從新老MQ接收消息的能力,這就是所謂的“雙訂閱”:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

接下來對生産者進行釋出,使其往新MQ發送消息,等原MQ上堆積的所有消息被消費者接收并成功處理後,就可以對原MQ下線:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

在對原MQ下線的時候,因為消費者還保持着雙訂閱的狀态,是以最好先切斷消費者與原MQ的連接配接,再關閉原MQ,否則會造成一些異常(取決于SDK的實作)。如果消費者的訂閱邏輯實作的足夠優雅,可以在不重新開機消費者的情況下,通過一個指令線上切斷消費者與原MQ的連接配接。

這個方案看似可以用一種對業務無損的方式線上遷移MQ,在實際操作中可行性卻很低,其根本原因在于:同時從兩個MQ接收消息的改造工作量極大。一般情況下,每一個消費者都在引入MQ産品對應的SDK,并通過MQ提供接入點與MQ建立連接配接後,接下來就隻需要圍繞業務邏輯完成所需要的消息訂閱操作。這個時候要想同時從一個新的MQ接入消息,需要在代碼層面對所有的訂閱邏輯進行改造,這是一項非常複雜的工作。

在新MQ和原MQ是不同消息隊列産品的情況下,消費者需要同時引入兩套不同的SDK,改造難度會變得更大。基于雙訂閱方案完成MQ的遷移後,還需要考慮将來清理掉消費者從原MQ接收處理消息的遺留代碼,這也是需要一定工作量的。

如果在MQ和消費者中間,能有一個中間件來實作雙訂閱的邏輯,是不是消費者的代碼就不需要改造呢?答案是肯定的。但引入這樣的中間件本身就是一項非常有挑戰的工作,還增加了整個系統的複雜度,如果僅僅是為了MQ的線上遷移而引入一個新的元件,是得不償失的。

基于工作量和風險的考慮,盡量不要使用雙訂閱方案。

最優方案

雙訂閱的本質,在于存在一個消費者可以同時接收新舊兩個MQ消息的中間狀态,在這個狀态下,不管生産者往哪個MQ發送消息,消息都可以得到及時的處理。能不能有一種更簡單的方式讓消費者可以同時接收新舊兩個MQ的消息呢?當然有,而且實作方式更加的簡單。

在一個可靠的分布式微服務系統中,應用都可以通過增加節點的方式而進行水準擴容,為了確定整套系統的高可用性,每一個應用都不應該長期處于單執行個體運作狀态,而是通過多個無狀态的應用執行個體組成一個應用叢集。是以,在真實環境下,不管是消息的生産者還是消費者,都至少有2個執行個體在運作。在遷移MQ的過程中,“消費者可以同時接收新舊兩個MQ的消息”的中間狀态,并不一定要讓消費者的每一個執行個體都通過雙訂閱來實作,其實隻要讓一部分執行個體從原MQ接收消息,另一部分執行個體從新MQ接收消息就能滿足了。通過這樣的思路,能極大程度上簡化MQ遷移的工作量,而且在遷移的過程中確定業務不受任何影響。

在遷移之前,需要先把中繼資料資訊從原MQ複制到新MQ叢集,不同的消息隊列産品之前中繼資料的格式不一樣,需要根據業務場景進行中繼資料的轉換,中繼資料包括Topic、Queue、消費組、基礎配置等資訊。

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

首先,通過灰階釋出機制,讓一部分消費執行個體連上新的MQ。如果之前的消費者是單執行個體,這個時候也可以增加一個新的消費執行個體來完成這個步驟:

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

接下來,讓生産者往新的MQ發送消息,這個操作并不一定需要采取一刀切的方式,也可以通過灰階釋出的方式讓消息的轉向逐漸轉移到新的MQ上來。最終,原MQ将不再接收新的消息,它上面堆積的消息總将會被成功接收處理,這個時候可以繼續通過灰階釋出的方式解除消費者與原MQ的連接配接,連接配接全部解除完之後,原MQ就可以關閉了。

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

在這個方案中,對于單個的生産者和消費者,都不存在同時連接配接新舊兩個MQ的情況,是以在改造工作量非常小。而且遷移的過程通過灰階的方式實作,既不會影響業務,又可以進一步的降低風險,是消息隊列線上遷移的通用方案。

更複雜的場景

在實際的系統架構中,應用之間的關系會更為複雜,主要展現在3個方面:

  1. 整個系統中存在多個應用以及消息隊列之間的互相互動,形成複雜的網狀結構。
  2. 兩個應用互為生産者消費者。
  3. 多個應用訂閱同一個主題,進而發往該主題的每個消息都會被多個應用收到。
消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

在這種複雜的場景中,本文推薦的消息隊列遷移最優方案是否還能适用呢?答案是肯定的。任何一個大型系統都是由若幹子系統構成,隻要我們按照一定的規律和順序,對其中的子系統逐個擊破,就能化繁為簡,解決問題。

首先,我們需要對整個系統進行梳理,找到每一個應用之間的互相關系,畫出一張完整的架構圖是遷移過程中必不可少的步驟。接下來,對其中需要遷移的消息隊列進行編号,嚴格按照順序執行線上遷移操作,對每一個消息隊列的遷移,都遵循本文所提到的最優方案依次進行。一個遷移隊列完全遷移成功後,再遷移下一個消息隊列,避免彼此的操作步驟當中存在重疊交錯的部分。

對于兩個應用互為生産者和消費者的情況,我們應該将遷移工作按照消息的流向拆分成兩步。比如對于示例中的應用A和應用B,可以先處理從A發往B的消息。同樣,為了降低風險,確定遷移過程中業務不受到影響,灰階方案是有必要采用的,這樣就需要消息方應用B擁有多個對等的執行個體。

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題

對于多個應用訂閱同一個主題的情況,也可以采用類似的思路進行處理,唯一需要注意的是原消息隊列上的存留的消息,都被每一個消費者成功接收并成功消費之後,才能将原消息隊列下線。

總結下來,消息隊列線上遷移的完整步驟為:

  1. 對系統整體架構進行梳理,分步依次執行,避免不同的消息隊列之間的遷移存在重疊。
  2. 中繼資料遷移。
  3. 确儲存在一個中間狀态:對于所有消費者,都有對等的執行個體同時連接配接新舊MQ。
  4. 讓消息生産者往新的MQ發送消息。
  5. 等原MQ上所有存留的消息都被每一個消費者成功接收并成功消費之後,下線原MQ。

常見問題

問: 為什麼不在新舊MQ之間進行消息資料的同步?

答:對于MQ而言,消息一旦被消息者接收并得到處理,就不再是持久化的資料,可以直接删除或歸檔。在遷移的過程中,新舊MQ之間消息資料同步是沒有必要的,反而會增加遷移的難度,并導緻消息被重複接收。

問:遷移過程中需要驗證消息幂等性嗎?

答: RocketMQ、Kafka等大多數消息隊列産品都沒有從消息隊列服務端本身確定消息隻投遞一次,需要消費者自行實作對幂等性的保證。是以,不管在消息隊列的遷移過程中,還是正常使用中,都應該借助資料庫、Redis等外部系統確定消息的幂等性,否則會造成業務邏輯重複處理。

【更多精彩】

1.中間件爆款一折起,還有阿裡巴巴十年最佳實踐深度解密,點選馬上了解:

https://www.aliyun.com/activity/daily/commercial?spm=5176.20960838.0.0.6a54305etoEn4D

2.【填問卷領淘公仔】點選馬上填寫問卷:

https://survey.aliyun.com/apps/zhiliao/YmW95Gk8bU

【加入行業實戰交流釘釘群】

阿裡雲專門成立了“網際網路架構更新實戰課”釘釘群,每周邀請一位阿裡雲專家在群内進行行業最佳實踐直播,每天分享行業前沿幹貨,釘釘掃碼馬上加入。

消息隊列線上遷移實戰 | 最佳實踐前言理論基礎在系統維護期停業務遷移雙訂閱方案最優方案更複雜的場景常見問題