分佈式消息隊列XXL-MQ

一、簡介

1.1 概述

XXL-MQ是一款輕量級分佈式消息隊列,支持串行、並行和廣播等多種消息模型。現已開放源代碼,開箱即用。

支持三種消息模式:

消息模式 特徵說明 適用場景
TOPIC(廣播消息)模型 發佈/訂閱模式, 一條消息將會廣播發送給對應Topic下所有在線的Consumer 如廣播集羣節點進行緩存更新、廣播集羣節點進行站點靜態化等
QUEUE(併發隊列)模型 點對點模式, 消息進去隊列之後, 只會被消費一次。同一Topic下的多個Consumer並行消費消息, 吞吐量較大 如郵件發送、短信發送等業務邏輯
SERIAL_QUEUE(串行隊列)模型 點對點模式, 消息進去隊列之後, 只會被消費一次。 但是,同一個Topic下只會有一個Consumer串行消費消息, 適用於嚴格限制併發的場景 秒殺、搶單等排隊業務邏輯

至今,XXL-MQ已接入多家公司的線上產品線,截止2016-09-18爲止,XXL-MQ已接入的公司包括不限於:

- 1、農信互聯
- ……

1.2 特性

  • 1、簡單易用: 一行代碼即可發佈一條消息; 一行註解即可訂閱一個消息主題;
  • 2、部署簡單: 除ZK之外不依賴第三方服務, 基於Netty + Zookeeper實現;
  • 3、三種消息模式: TOPIC(廣播消息)模型、QUEUE(併發隊列)模型 和 SERIAL_QUEUE(串行隊列)模型,下文將會詳細講解:
  • 4、Broker集羣、HA: Broker支持集羣部署, 可大大提高系統可用性,以及消息吞吐能力;
  • 5、吞吐量: 依賴於部署的Broker集羣和Mysql性能;
  • 5、消息可追蹤: 支持追蹤每一條消息的執行路徑, 便於排查業務問題;
  • 6、消息可見: 系統中每一條消息可通過Web界面在線查看,甚至支持編輯消息內容和消息狀態;
  • 7、一致性: QUEUE(併發隊列)模型 和 SERIAL_QUEUE(串行隊列)模型的消息,保證只會成功執行一次;
  • 8、Delay執行: 支持設置消息的延遲生效時間, 到達設置的Delay執行時間時該消息纔會被消費 ,提供DelayQueue的功能;
  • 9、消息重試: 支持設置消息的重試次數, 在消息執行失敗後將會按照設置的值進行消息重試執行,直至重試次數耗盡或者執行成功;

1.3 背景

Why MQ

  • 異步: 很多場景下,不會立即處理消息,此時可以在MQ中存儲message,並在某一時刻再進行處理;
  • 解耦: 不同進程間添加一層實現解耦,方便今後的擴展。
  • 消除峯值: 在高併發環境下,由於來不及同步處理,請求往往會發生堵塞,比如大量的insert,update之類的請求同時到達mysql,直接導致無數的行鎖表鎖,甚至最後請求會堆積過多,從而觸發too manyconnections錯誤。通過使用消息隊列,我們可以異步處理請求,從而緩解系統的壓力。
  • 耗時業務: 在一些比較耗時的業務場景中, 可以耗時較多的業務解耦通過異步隊列執行, 提高系統響應速度和吞吐量;

Why XXL-MQ

目前流行的ActiveMQ、RabbitMQ和ZeroMQ等消息隊列的軟件中,大多爲了實現AMQP,STOMP,XMPP之類的協議,變得極其重量級(如新版本Activemq建議分配內存達1G+),但在很多Web應用中的實際情況是:我們只是想找到一個緩解高併發請求的解決方案,一個輕量級的消息隊列實現方式纔是我們真正需要的。

1.4 下載

文檔地址

源碼倉庫地址

源碼倉庫地址 Release Download
https://github.com/xuxueli/xxl-mq Download
https://gitee.com/xuxueli0323/xxl-mq Download

技術交流

中央倉庫地址

<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-mq-client</artifactId>
    <version>{最新Release版本}</version>
</dependency>

1.5 環境

  • Maven3+
  • Jdk1.7+
  • Tomcat7+
  • Mysql5.5+
  • Zookeeper3.4+

二、系統設計

2.1 系統架構圖

輸入圖片說明

角色解釋:

  • Message : 消息實體;
  • Broker : 消息代理中心, 負責連接Producer和Consumer;
  • Topic : 消息主題, 每個消息隊列的唯一性標示;
  • Topic segment : 消息分段, 同一個Topic的消息隊列,將會根據訂閱的Consumer進行分片分組,每個Consumer擁有的消息片即一個segment;
  • Producer : 消息生產者, 綁定一個消息Topic, 並向該Topic消息隊列中生產消息;
  • Consumer : 消息消費者, 綁定一個消息Topic, 只能消費該Topic消息隊列中的消息;
  • Consumer Group : 訂閱同一個Topic的所有Consumer,認定爲一個分組;

架構圖模塊解讀:

  • 1、Server
    • 1.1、Broker: 消息代理中心, 系統核心組成模塊, 負責接受消息生產者Producer推送生產的消息, 同時負責提供RPC服務供消費者Consumer使用來消費消息;
    • 1.2、Message Queue: 消息存儲模塊, 目前底層使用mysql消息表;
  • 2、Registry Center
    • 2.1、Broker Registry Center: Broker註冊中心子模塊, 供Broker註冊RPC服務使用;
    • 2.2、Consumer Registry Center: Consumer註冊中心子模塊, 供Consumer註冊消費節點使用;
  • 3、Client
    • 3.1、Producer: 消息生產者模塊, 負責提供API接口供開發者調用,並生成和發送隊列消息;
    • 3.2、Consumer: 消息消費者模塊, 負責訂閱消息並消息;

2.2 Message設計: 消息核心參數

  • id: 消息唯一標識
  • name: 消息主題Topic, 每個消息隊列的唯一標識
  • delayTime: 消息延遲執行的時間, 當前時間超過延遲執行時間該消息纔會被消費掉, new Date()立即執行, 否則在延遲時間點之後開始執行;
  • status: 消息狀態: NEW=新消息、ING=消費中、SUCCESS=消費成功、FAIL=消費失敗
  • msg: 消息歷史流轉日誌
  • retryCount: 剩餘重試次數, 在消息執行失敗後將會按照設置的值進行消息重試執行,直至重試次數耗盡或者執行成功;

2.3 Broker設計

Broker(消息代理中心), 系統核心組成模塊, 負責接受消息生產者Producer推送生產的消息, 同時負責提供RPC服務供消費者Consumer使用來消費消息;

Broker支持集羣部署, 集羣節點之間地位平等, 集羣部署情況下可大大提高系統的消息吞吐量。

Broker通過Zookeeper實現集羣功能, 各節點在啓動時會自動註冊到註冊中心, Producer或Consumer在生產消息或者消費消息時,將會通過ZK自動感知到在線的Broker節點。

Broker在接收到Produce的生產消息的RPC調用時, 並不會立即存儲該消息, 而是立即push到內存隊列中, 同時立即響應RPC調用。 內存隊列將會異步將隊列中的消息數據存儲到Mysql中。

Broker在接收到 "消息鎖定" 等同步RPC調用時, 將會觸發同步調用, 採用樂觀鎖方式鎖定消息;

2.4 Producer設計

Producer(消息生產者), Producer可以生成三種模式的消息

  • TOPIC(廣播消息)模型 : Producer生產該類型消息, 主要通過ZK來實現, 可結合 章節2.6.1 來理解;
  • QUEUE(併發隊列)模型 : Producer生產該類型消息, 主要通過Broker的RPC服務來實現, 沒生產一條消息,將會向Broker集羣發送一條RPC調用, Broker將會立即將消息加入內存隊列響應RPC調用, 內存隊列將會異步將該消息存儲在Mysql中;
  • SERIAL_QUEUE(串行隊列)模型 : 同 "QUEUE(併發隊列)模型" 邏輯;

2.5 Registry Center設計

Registry Center(註冊中心)主要分爲兩個子模塊: Broker註冊中心、Consumer註冊中心;

  • Broker註冊中心子模塊: ZK中指定有一個固定的Broker註冊位置, 每個Broker節點將會在該固定位置新增一個 "EPHEMERAL" 類型的ZK子節點, 賦值爲該Broker的地址, 因此, Producer或Consumer可自動感知在線的節點來生產或消費消息;
  • Consumer註冊中心子模塊: 每個消息主題Topic在ZK中對應一個指定的註冊位置, 該消息主題下的Consumer將會在該位置新增一個 "EPHEMERAL" 類型的ZK子節點, 賦值爲該Consumer的唯一ID。 因此, 每個Consumer可以感知同一Topic下在線的所有Consumer以及排序, 這在 "QUEUE(併發隊列)模型" 消息的分片中, 以及 "SERIAL_QUEUE(串行隊列)模型" 消息的串行執行中將會起到關鍵性作用,下文將會詳細講解;

2.6 Consumer設計: 三種核心消息模型剖析

2.6.1 TOPIC(廣播消息)模型

"TOPIC(廣播消息)模型" 通過ZK來實現, 該類型消息在"Registry Center" 中分配一個指定的消息監聽節點, Producer通過對該節點賦值觸發ZK的NodeDataChanged廣播, 而該消息隊列的Consumer在啓動時會主動監聽該節點, 在監聽到NodeDataChanged時將會根據節點數據自動生成一條廣播消息並執行。從而,實現了消息廣播功能;

因爲通過ZK節點來傳輸消息數據, 因此消息數據大小不可超過1M, 底層對消息長度做了限制。同時, 廣播類型消息將不會落地;

該類型Consumer內部爲了一個內存隊列, ZK的NodeDataChanged觸發生成的廣播將會推送到對應Consumer的內存隊列中異步執行, 因此廣播消息並不會堵塞, 而且可保證消息串行平穩執行。

2.6.2 QUEUE(併發隊列)模型

"QUEUE(併發隊列)模型" 通過 "多線程輪訓 + 消息分片 + PULL + 消息鎖定" 的方式來實現:

  • 多線程輪訓: 該模式下每個Consumer將會存在一個線程, 如存在多個Consumer, 多個Consumer將會並行消息同一主題下的消息, 大大提高消息的消費速度;
  • 消息分片 : 隊列中消息將會按照 "Registry Center" 中註冊的Consumer列表順序進行消息分段, 保證一條消息只會被分配給其中一個Consumer, 每個Consumer只會消費分配給自己的消息。 因此在多個Consumer併發消息時, 可以保證同一條消息不被多個Consumer競爭來重複消息。
    • 分片函數: MOD("消息主鍵ID", #{在線消費者總數}) = #{當前消費者排名} ,
    • 分片邏輯解釋: 每個Consumer通過註冊中心感知到在線所有的Consumer, 計算出在線Consumer總數total, 以及當前Consumer在所有Consumer中的排名rank; 把消息主鍵ID對在線Consumer總數total進行取模, 餘數和當前Consumer排名rank一致的消息認定爲分配給自己的消息;
  • PULL : 每個Consumer將會輪訓PULL消息分片分配給自己的消息, 順序消費。
  • 消息鎖定: Consumer在消費每一條消息時,將會主動進行消息鎖定, 通過數據庫樂觀鎖來實現, 鎖定成功後消息狀態變更爲執行中狀態, 將不會被Consumer再次PULL到。因此, 可以更進一步保證每條消息只會被消費一次;
  • 消息狀態和日誌: 消息執行結束後, 將會調用Broker的RPC服務修改消息狀態並追加消息日誌, Broker將會通過內存隊列方式, 異步消息隊列中變更存儲到數據庫中。

2.6.3 SERIAL_QUEUE(串行隊列)模型

"SERIAL_QUEUE(串行隊列)模型" 通過 "單線程輪訓 + PULL" 的方式來實現,

  • 單線程輪訓: 該模式下, Consumer可以通過 "Registry Center" 感知到在線的所有Consumer, 規定只有最大的Consumer節點對應的線程擁有執行權限, 其餘節點將會進入睡眠狀態, 保證只會有一個Consuemr消費隊列中數據;
  • PULL : 該模式下, 只會有一個存活狀態的Consumer, 因此隊列中所有消息都會被分配給該Consumer, 該Consumer將會輪訓獲取一定數量的消息, 順序消費;
  • 消息鎖定: 同 "QUEUE(併發隊列)模型" 邏輯;
  • 消息狀態和日誌: 同 "QUEUE(併發隊列)模型" 邏輯;

2.8 消息重試

Delay : 支持設置消息的延遲生效時間, 到達設置的Delay執行時間時該消息纔會被消費 ,提供DelayQueue的功能;

2.7 消息延遲執行

當狀態爲執行失敗的消息, 並且剩餘重試次數大於零時, Broker竟會扣減一次剩餘重試次數, 同時將失敗的狀態改爲初始狀態並記錄重試日誌, 初始狀態的消息將會被Consumer重新消息。

三、快速入門

3.1 編譯項目

輸入圖片說明

源碼目錄介紹:

  • /db
  • /doc
  • /xxl-mq-broker (消息代理中心, 同時提供消息在線管理功能)
  • /xxl-mq-client (公共依賴, 提供API開發Producer和Consumer)
  • /xxl-mq-example (消息生產和消費example示例項目, 項目中開發了三種經典的消息模型, 可自行參考學習並使用)

3.2 初始化數據庫

執行源碼目錄下SQL腳本 "/xxl-mq/doc/db/xxl-mq-mysql.sql" , 初始化MQ數據庫表;

3.3 配置全局Zookeeper地址

"Broker項目" 和 "XXL-MQ接入項目", 使用同樣的方式進行Zookeeper地址配置, 配置文件並不在項目中, 而在項目所在硬盤指定絕對目錄中, 便於ZK配置文件統一

配置文件配置在項目所在硬盤絕對地址: “/data/webapps/xxl-conf.properties”

配置內容如下:

// zookeeper集羣時,多個地址用逗號分隔
zkserver=127.0.0.1:2181

3.4 配置部署“消息代理中心”(支持集羣部署)

配置JDBC連接

輸入圖片說明

配置登錄賬號密碼

輸入圖片說明

3.5 接入XXL-MQ並使用 (以示例項目"xxl-mq-example"爲例,進行講解)

加入XXL-MQ的maven依賴

輸入圖片說明

生產消息

  • 1、生產TOPIC廣播消息
    XxlMqProducer.broadcast("消息主題", "消息數據, Map<String, String>格式");

輸入圖片說明

  • 2、生產QUEUE、SERIAL_QUEUE消息 (QUEUE和SERIAL_QUEUE兩種消息格式完全一樣, 生產消息的方式相同; 不同之處在於Consumer測配置不同)
    XxlMqProducer.produce("消息主題", "消息數據, Map<String, String>格式");

輸入圖片說明

消費消息

( 如果系統僅僅負責生產消息, 可忽略掉該配置; )

  • 1、配置Consumer工廠, 掃描 "MqConsumer" 目錄

輸入圖片說明

  • 2、開發 "MqConsumer"

系統中每個消費者以 "MqConsumer" 的形式存在, 規定如下:

 - 1、每個 "MqConsumer" 需要繼承 "com.xxl.mq.client.consumer.IMqConsumer" 接口;
 - 2、需要掃描爲Spring的Bean實例, 如加上 "@Service" 註解並被Spring掃描;
 - 3、需要加上註解 "com.xxl.mq.client.consumer.annotation.MqConsumer"。該註解 "value" 值爲訂閱的消息主題, "type" 值爲消息類型(TOPIC廣播消息、QUEUE併發消息隊列 和 SERIAL_QUEUE串行消息隊列);

系統中已經提供了 (TOPIC、QUEUE和SERIAL_QUEUE) 三種模式消息Consumer的示例, 參考如下:

"QUEUE併發消息隊列" 模式的Consumer開發示例:

輸入圖片說明

"SERIAL_QUEUE串行消息隊列" 模式的Consumer開發示例:

輸入圖片說明

"TOPIC廣播消息" 模式的Consumer開發示例:

輸入圖片說明

測試

示例項目(xxl-mq-example)已經提供了三種格式消息的 "消息生成示例代碼" 和 "消息消費示例代碼", 本次測試在此基礎上進行;

我在本地測試時: 啓動兩臺Tomcat-8080和Tomcat-8081, 端口分別爲8080和8081, 各自都部署 "xxl-mq-example 消息生產和消費示例項目" 和 "xxl-mq-broker 消息代理中心項目";

"xxl-mq-example 消息生產和消費示例項目" 部署在根路徑下, 訪問地址爲: http://localhost:8080/ , 可以在線查看消息 QUEUE和ERIAL_QUEUE 消息記錄,並且可以對消息進行 "查詢(統計某個消息主題下消息堆積情況,消費情況)"、"新增"、"編輯(失敗消息修改重試次數進行重試, 修改消息數據, 修改消息Delay執行時間從而讓消息在指定時間後才執行)"和"刪除"等操作;

輸入圖片說明

"xxl-mq-broker 消息代理中心項目" 部署在二級路徑 "/example" 下, 訪問地址 http://localhost:8080/example/ 可進入示例項目提供的三種消息的發送界面, 在界面上點擊按鈕,即可生成三種消息, 可以跟蹤消費方消費日誌跟蹤消息消費情況;

輸入圖片說明

1、測試 "QUEUE (並行消費隊列)" :

操作: 訪問 "xxl-mq-broker 消息代理中心項目" 進入提供的消息生產測試頁面, 點擊 "QUEUE (並行消費隊列)= mqconsumer-01" 按鈕

現象: 進入 "消息代理中心", 如下圖點擊每條消息對應的 "歷史流轉日誌" 按鈕, 可查看每一條消息的流轉信息;

輸入圖片說明

輸入圖片說明

說明: 上圖所示, 第一: 消息狀態爲SUCCESS, 說明消費成功; 第二: 消息ID=903, "rank=1, total=2", 意思是當前該消息隊列存在兩個Consumer, 該消息被排序爲1(排名從0開始)的的Consumer消費掉; 通過上文 "QUEUE" 消息分片邏輯可知, 該消息ID對總consumer取模餘數爲1, 可消費該消息的Consumer的排名一致,說明消息分片成功;

2、測試 "SERIAL_QUEUE (串行消費隊列)" :

操作: 訪問 "xxl-mq-broker 消息代理中心項目" 進入提供的消息生產測試頁面, 點擊 "SERIAL_QUEUE (串行消費隊列)= mqconsumer-02" 按鈕

現象: 進入 "消息代理中心", 如下圖點擊每條消息對應的 "歷史流轉日誌" 按鈕, 可查看每一條消息的流轉信息;

輸入圖片說明

輸入圖片說明

說明: 上圖所示, 第一: 消息狀態爲SUCCESS, 說明消費成功; 第二: 消息ID=910, "rank=0, total=1", 意思是當前該消息隊列存在1個存活的Consumer, 該消息被排序爲0(排名從0開始)的的Consumer消費掉; 通過上文 "SERIAL_QUEUE" 消費邏輯可知, 該類型消息對應的Consumer只有一個處於存活狀態, 雖然兩個Tomcat集羣部署,但是隻有一個處於存活狀態, 它將串行消費掉隊列中所有消息, 說明串行消費成功;

3、測試 "TOPIC (廣播消息)" :

操作: 訪問地址 http://localhost:8080/example/ ,點擊 "TOPIC (廣播消息)= mqconsumer-03" 按鈕

現象: 兩臺Tomcat-8080和Tomcat-8081下, 都打印了以下日誌,

2016-09-11 22:37:10 xxl-mq-example [com.xxl.mq.example.mqcomsumer.DemoCMqComsumer]-[Thread-18]-[consume]-[25]-[INFO] TOPIC(廣播消息): mqconsumer-02消費一條消息:{"時間戳":"1473604630889"}

說明: "TOPIC (廣播消息)" 發送成功, 監聽該消息主題 "mqconsumer-03" 的 "DemoCMqComsumer" 都收到了廣播消息並執行成功, 說明測試成功;

四、版本更新日誌

4.1 版本V1.1.0 新特性

  • 1、簡單易用: 一行代碼即可發佈一條消息; 一行註解即可訂閱一個消息主題;
  • 2、部署簡單: 除ZK之外不依賴第三方服務;
  • 3、三種消息模式: TOPIC(廣播消息)模型、QUEUE(併發隊列)模型 和 SERIAL_QUEUE(串行隊列)模型,下文將會詳細講解:
  • 4、Broker集羣、HA: Broker支持集羣部署, 可大大提高系統可用性,以及消息吞吐能力;
  • 5、吞吐量: 依賴於部署的Broker集羣和Mysql性能;
  • 5、消息可追蹤: 支持追蹤每一條消息的執行路徑, 便於排查業務問題;
  • 6、消息可見: 系統中每一條消息可通過Web界面在線查看,甚至支持編輯消息內容和消息狀態;
  • 7、一致性: QUEUE(併發隊列)模型 和 SERIAL_QUEUE(串行隊列)模型的消息,保證只會成功執行一次;
  • 8、Delay執行: 支持設置消息的延遲生效時間, 到達設置的Delay執行時間時該消息纔會被消費 ,提供DelayQueue的功能;
  • 9、消息重試: 支持設置消息的重試次數, 在消息執行失敗後將會按照設置的值進行消息重試執行,直至重試次數耗盡或者執行成功;

4.2 版本V1.1.1 特性

  • 1、項目groupId改爲com.xuxueli,爲推送maven中央倉庫做準備;
  • 2、項目推送Maven中央倉庫;
  • 3、底層系統優化,CleanCode等;
  • 4、修復confirm和alert彈框衝突導致消息列表錯亂的問題;
  • 5、優化ZK註冊邏輯,ZK註冊基礎路徑提前初始化;
  • 6、broadcast 廣播消息時ZK 發送方不進行watch, 否則發送方也會監聽到;
  • 7、修復一處因ReentrantLock導致可能死鎖的問題;

4.3 版本V1.1.2 [迭代中]

  • 1、client端與Broker長鏈初始化優化,防止重複創建連接。

TODO

  • Queue消息分組:每個主題可對應多個分組,topic》group1、group2》,每個分組下對應多個消費者,consumer01、consumer02;Queue消息生產時,將會羣發給在線的所有主題下的分組列表,consumer只消費自己分組下的;不同分相互隔離;
  • Queue模型Consumer路徑, 和 Topic模型消息廣播節點路徑, 進行拆分, 避免相互耦合的可能;
  • Broker中過期消息自動清理: 新增參數cache_day, 單位/天, 含義: 針對消費成功的消息, Broker緩存時間爲cache_day天, 超過cache_day的消費成功的消息, 將會被刪除;
  • 消息堆積報警: 新增參數alarm_num: 消息堆積報警, 30分鐘統計一次消息情況, 將會根據topic分組, 堆積超過閾值的topic將會在報警郵件報表中進行記錄;
  • Client端,LocalQueue,max=50條/批次,批量push;Server端,LocalQueue,max=50條/批次,批量入庫;
  • LocalQueue消息可能丟失,考慮LocalFile;
  • producer消息,推送broker失敗,先緩存本次文件;
  • producer消息,生成UUID,推送失敗重複推送,同時避免重複;

五、其他

5.1 項目貢獻

歡迎參與項目貢獻!比如提交PR修復一個bug,或者新建 Issue 討論新特性或者變更。

5.2 用戶接入登記

更多接入的公司,歡迎在 登記地址 登記,登記僅僅爲了產品推廣。

5.3 開源協議和版權

產品開源免費,並且將持續提供免費的社區技術支持。個人或企業內部可自由的接入和使用。

  • Licensed under the GNU General Public License (GPL) v3.
  • Copyright (c) 2015-present, xuxueli.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章