文章轉載自:http://www.mydlq.club/article/97/
系統環境:
- 系統版本:CentOS 7.8
- RocketMQ 版本:4.7.0
- Docker 版本:19.03.13
一、RocketMQ 簡介
RocketMQ 是一個由 Java 語言編寫的分布式高性能消息中間件,由阿里創建,后將其開源給 Apache 基金會,現在已經成為 Apache 開源項目中的頂級開源項目,具有高性能、高可靠、高實時、分布式特點,尤其內部封裝了很多跟業務相關的功能模塊,能讓我們快速用其解決業務上消息處理的一些難點,所以如何使用與部署 RocketMQ 是這里要講的重點。這里記錄一下如果通過 Docker 部署 RocketMQ Dledger 集群模式這個過程。
二、RocketMQ 需部署的組件
這里介紹下 RocketMQ 相關的組件:
- 名字服務(Name Server): 是一個幾乎無狀態節點,可集群部署,集群節點間相互獨立沒有信息交換。其功能主要為更新和發現 Broker 服務,生產者或消費者能夠通過其查找到各主題相應的 Broker IP 列表。
- 代理服務(Broker Server): 消息中轉角色,負責存儲消息,轉發消息。分為 Master Broker 和 Slave Broker,一個 Master Broker 可以對應多個 Slave Broker,但是一個 Slave Broker 只能對應一個 Master Broker。Broker 啟動后需要完成一次將自己注冊至 Name Server 的操作,隨后每隔 30s 定期 向Name Server 上報 Topic 路由信息。
- 生產者: 與 Name Server 集群中的其中一個節點(隨機)建立長鏈接(Keep-alive),定期從 Name Server 讀取 Topic 路由信息,並向提供 Topic 服務的 Master Broker 建立長鏈接,且定時向 Master Broker 發送心跳。
- 消費者: 與 Name Server 集群中的其中一個節點(隨機)建立長連接,定期從 Name Server 拉取 Topic 路由信息,並向提供 Topic 服務的 Master Broker、Slave Broker建立長連接,且定時向 Master Broker、Slave Broker 發送心跳。Consumer 既可以從 Master Broker 訂閱消息,也可以從 Slave Broker 訂閱消息,訂閱規則由 Broker 配置決定。
- 控制台(console): RocketMQ 的 Web 可視化控制台,包含 RocketMQ 常用操作,可以用於簡單管理 RocketMQ 平台。
三、RocketMQ 常用部署模式
RocketMQ 常用部署方案有以下幾種:
- 單機模式
- 多主模式
- 雙主雙從/多主多從模式(異步復制)
- 雙主雙從/多主多從模式(同步雙寫)
- Dledger 集群模式
(1)、單機模式
這種模式就如該名單機模式一樣,就是部署單個 RocketMQ Broker 來使用,一般使用這種方式在生產中風險較大,一旦 Broker 重啟或者宕機時,會導致整個服務不可用,所以常常在學習、開發過程中才會使用這種模式。
優缺點分析:
優點: 本地開發測試,配置簡單,同步刷盤消息不會丟失。
缺點: 不可靠,如果宕機會導致服務不可用。
(2)、多主模式
全部由 Broker Master 節點組成(即可能部署兩個或者更多 Broker),生產者發送的數據會分別存入不同的 Broker 中,這樣能夠避免某個 Broker 一直接收處理數據從而負載過高。
優缺點分析:
優點: 性能高,配置簡單,單個 Master 宕機或重啟維護對應用無影響,在磁盤配置為 RAID10 時,即使機器宕機不可恢復,由於 RAID10 磁盤非常可靠,消息也不會丟(異步刷盤可能會丟失少量消息,同步刷盤能保證消息不丟)。
缺點: 單台服務器宕機期間,不可訂閱該服務器上未被消費者消費的消息,只有機器恢復后才可恢復訂閱,所以可能會影響消息的實時性。
(3)、雙主雙從/多主多從模式(異步復制)
一般會部署多個 Broker Master,同時也會為各個 Broker Master 部署一個或者多個 Broker Slave,且 Master 和 Slave 之間采用"異步復制數據"方式進行數據同步(主從同步消息會有延遲,毫秒級),這樣在生產者將消息發送到 Broker Master 后不必等待數據同步到 Slave 節點,就返回成功。
優缺點分析:
優點: 性能高,且磁盤損壞也不會丟失大量消息,消息實時性不會受影響,Master 宕機后,消費者仍然可以從 Slave 消費。
缺點: 主備有短暫消息延遲,毫秒級,如果Master宕機,磁盤損壞情況,會丟失少量消息。
(4)、雙主雙從/多主多從模式(同步雙寫)
一般會部署多個 Broker Master,同時也會為各個 Broker Master 部署一個或者多個 Broker Slave,且 Master 和 Slave 之間采用"同步復制數據"方式進行數據同步,這樣在生產者將消息發送到 Broker Master 后需要等待數據同步到 Slave 節點成功后,才返回成功。
優缺點分析:
優點: 數據與服務都無單點故障,Master 宕機情況下,消息無延遲,服務可用性與數據可用性都非常高;
缺點: 性能比異步復制模式略低(大約低10%左右),發送單個消息的 RT 會略高,且目前版本在主節點宕機后,備機不能自動切換為主機。
(5)、Dledger 集群模式
RocketMQ-on-DLedger Group 是指一組相同名稱的 Broker,至少需要 3 個節點,通過 Raft 自動選舉出一個 Leader,其余節點 作為 Follower,並在 Leader 和 Follower 之間復制數據以保證高可用。當 Master 節點出現問題宕機后也能自動容災切換,並保證數據一致性。該模式也支持 Broker 水平擴展,即可以部署任意多個 RocketMQ-on-DLedger Group 同時對外提供服務。
優缺點分析:
優點: 多節點(至少三個)組成集群,其中一個為 Leader 節點,其余為 Follower 節點組成高可用,能夠自動容災切換。
缺點: 需要 RocketMQ 4.5 及以后版本才支持。
四、RocketMQ Dledger 集群模式簡介
1、傳統部署方式的不足
在 RocketMQ 4.5 之前的版本中,部署 RocketMQ 高可用方案一般都會采用多主多從方式,這種方式需要多個 Master 節點與實時備份 Master 節點數據的 Slave 節點,Slave 節點通過同步復制或異步復制的方式去同步 Master 節點數據。但這樣的部署模式存在一定缺陷。比如故障轉移方面,如果 Master 點掛了,還需要人為手動對 Master 節點進行重啟或者切換,它無法自動的將 Slave 節點轉換為 Master 節點。因此,我們希望能有一個新的多副本架構,去解決這個問題。
2、新技術解決的問題
新的多副本架構首先需要解決自動故障轉移的問題,本質上來說問題關鍵點在於 Broker 如何自動推選主節點。這個問題的解決方案基本可以分為兩種:
- 利用第三方協調服務集群完成選主,比如 Zookeeper 或者 Etcd,但是這種方案會引入了重量級外部組件,使部署變得復雜,同時也會增加運維對組件的故障診斷成本,比如在維護 RocketMQ 集群還需要維護 Zookeeper 集群,保證 Zookeeper 集群如何高可用,不僅僅如此,如果 zookeeper 集群出現故障也會影響到 RocketMQ 集群。
- 利用 raft 協議來完成一個自動選主,raft 協議相比前者的優點是不需要引入外部組件,自動選主邏輯集成到各個節點的進程中,節點之間通過通信就可以完成選主。
RocketMQ 最終選擇使用 raft 協議來解決這個問題,而 DLedger 就是一個基於 raft 協議的 commitlog 存儲庫,也是 RocketMQ 實現新的高可用多副本架構的關鍵。
3、Dledger 簡介
分布式算法中比較常常聽到的是 Paxos 算法,但是由於 Paxos 算法難於理解,且實現比較苦難,所以不太受業界歡迎。然后出現新的分布式算法 Raft,其比 Paxos 更容易懂與實現,到如今在實際中運用的也已經很成熟,不同的語言都有對其的實現。Dledger 就是其中一個 Java 語言的實現,其將算法方面的內容全部抽象掉,這樣開發人員只需要關系業務即可,大大降低使用難度。
4、DLedger 定位
Raft 協議是復制狀態機的實現,這種模型應用到消息系統中就會存在問題。對於消息系統來說,它本身是一個中間代理,commitlog 狀態是系統最終狀態,並不需要狀態機再去完成一次狀態構建。因此 DLedger 去掉了 raft 協議中狀態機的部分,但基於raft協議保證commitlog 是一致的,並且是高可用的。
另一方面 DLedger 又是一個輕量級的 java library。它對外提供的 API 非常簡單,append 和 get。Append 向 DLedger 添加數據,並且添加的數據會對應一個遞增的索引,而 get 可以根據索引去獲得相應的數據。因此 DLedger 是一個 append only 的日志系統。
5、DLedger 應用場景
DLedger 其中一個應用就是在分布式消息系統中,RocketMQ 4.5 版本發布后,可以采用 RocketMQ on DLedger 方式進行部署。DLedger commitlog 代替了原來的 commitlog,使得 commitlog 擁有了選舉復制能力,然后通過角色透傳的方式,raft 角色透傳給外部 broker 角色,leader 對應原來的 master,follower 和 candidate 對應原來的 slave。
因此 RocketMQ 的 broker 擁有了自動故障轉移的能力,在一組 broker 中如果 Master 掛了,能夠依靠 DLedger 自動選主能力重新選出一個 leader,然后通過角色透傳變成新的 Master。
DLedger 還可以構建高可用的嵌入式 KV 存儲。我們把對一些數據的操作記錄到 DLedger 中,然后根據數據量或者實際需求,恢復到hashmap 或者 rocksdb 中,從而構建一致的、高可用的 KV 存儲系統,應用到元信息管理等場景。
6、RocketMQ Dledger 的方案簡介
RocketMQ-on-DLedger Group 是指一組相同名稱的 Broker,組中至少需要 3 個 Broker 節點來保證集群能夠運行,在 Broker 啟動時候,通過 raft 算法能夠自動選舉出一個 Broker 為 Leader 節點,其余為 Follower 節點。這種模式下 Leader 和 Follower 之間復制數據以保證高可用,如果 Leader 節點出現問題是可以自動進行容災切換並保證數據一致性。且不僅僅如此,該模式也支持 Broker 節點水平擴展來增加吞吐量。所以該模式將會是部署 RocketMQ 常用模式之一。
7、Dledger 集群單個節點示例配置
## RocketMQ 基本配置
brokerClusterName=RaftCluster
brokerName=RaftNode00
listenPort=30911
namesrvAddr=192.168.1.1:9876;192.168.1.2:9876
storePathRootDir=/tmp/rmqstore/node00
storePathCommitLog=/tmp/rmqstore/node00/commitlog
## Dledger 配置
dLegerSelfId=n0
dLegerGroup=RaftNode00
enableDLegerCommitLog=true
sendMessageThreadPoolNums=16
dLegerPeers=n0-192.168.1.1:40911;n1-192.168.1.2:40911;n2-192.168.1.3:40911
基本配置參數說明:
參數名稱 | 參數描述 | 參數示例 |
---|---|---|
brokerClusterName | Broker 集群名稱 | RaftCluster |
brokerName | Broker 名稱 | RaftNode00 |
listenPort | Broker 監聽端口 | 30911 |
namesrvAddr | Broker Namesrv 地址 | 192.168.1.1:9876;192.168.1.2:9876 |
storePathRootDir | Broker 存儲目錄 | /tmp/rmqstore/node00 |
storePathCommitLog | Commitlog 存儲目錄 | /tmp/rmqstore/node00/commitlog |
Dledger 配置參數說明:
五、部署 RcoketMQ Dledger 集群模式
1、部署服務器安排
服務器 | 部署的應用 | 物理資源 | 存儲掛載目錄 |
---|---|---|---|
192.168.2.21 | Namesrv Server、Broker、Console | 4C && 8G | /usr/local/rocketmq |
192.168.2.22 | Namesrv Server、Broker | 4C && 8G | /usr/local/rocketmq |
192.168.2.23 | Namesrv Server、Broker | 4C && 8G | /usr/local/rocketmq |
2、創建存儲數據的目錄
分別在三台服務器上創建下面目錄:
## 創建 Broker 持久化目錄
$ mkdir -p /usr/local/rocketmq/broker/conf && \
mkdir -p /usr/local/rocketmq/broker/logs && \
mkdir -p /usr/local/rocketmq/broker/store
## 創建 NameServer 持久化目錄
$ mkdir -p /usr/local/rocketmq/server/logs
3、創建 Broker 配置文件
在三台服務器上,分別創建如下對應的文件:
- 服務器一 192.168.2.21: 創建 RaftNode00 配置文件。
- 服務器二 192.168.2.22: 創建 RaftNode01 配置文件。
- 服務器三 192.168.2.23: 創建 RaftNode02 配置文件。
服務器一創建 RaftNode00 配置:
$ cat > /usr/local/rocketmq/broker/conf/broker.conf << EOF
brokerIP1=192.168.2.21
listenPort=30911
brokerClusterName=RaftCluster
brokerName=RaftNode00
namesrvAddr=192.168.2.21:9876;192.168.2.22:9876;192.168.2.23:9876
## DLeger
dLegerSelfId=n0
dLegerGroup=RaftNode00
enableDLegerCommitLog=true
## must be unique
dLegerPeers=n0-192.168.2.21:40911;n1-192.168.2.22:40911;n2-192.168.2.23:40911
sendMessageThreadPoolNums=2
EOF
服務器二創建 RaftNode01 配置:
$ cat > /usr/local/rocketmq/broker/conf/broker.conf << EOF
brokerIP1=192.168.2.22
listenPort=30911
brokerClusterName=RaftCluster
brokerName=RaftNode00
namesrvAddr=192.168.2.21:9876;192.168.2.22:9876;192.168.2.23:9876
## DLeger
dLegerSelfId=n1
dLegerGroup=RaftNode00
enableDLegerCommitLog=true
## must be unique
dLegerPeers=n0-192.168.2.21:40911;n1-192.168.2.22:40911;n2-192.168.2.23:40911
sendMessageThreadPoolNums=2
EOF
服務器三創建 RaftNode02 配置:
$ cat > /usr/local/rocketmq/broker/conf/broker.conf << EOF
brokerIP1=192.168.2.23
listenPort=30911
brokerClusterName=RaftCluster
brokerName=RaftNode00
namesrvAddr=192.168.2.21:9876;192.168.2.22:9876;192.168.2.23:9876
## DLeger
dLegerSelfId=n2
dLegerGroup=RaftNode00
enableDLegerCommitLog=true
## must be unique
dLegerPeers=n0-192.168.2.21:40911;n1-192.168.2.22:40911;n2-192.168.2.23:40911
sendMessageThreadPoolNums=2
EOF
4、下載相關鏡像
在三台服務器上,分別創建拉取對應的鏡像:
- 服務器一 192.168.2.21: 拉取 rocketmq 與 rocketmq-console 鏡像。
- 服務器二 192.168.2.22: 拉取 rocketmq 鏡像。
- 服務器三 192.168.2.23: 拉取 rocketmq 鏡像。
三台服務器分別拉取 RocketMQ 鏡像:
## 下載 RocketMQ 鏡像
$ docker pull foxiswho/rocketmq:4.7.0
第一台服務器拉取 RocketMQ Console 鏡像:
## 下載 RocketMQ 控制台
$ docker pull apacherocketmq/rocketmq-console:2.0.0
5、查看鏡像設置的用戶與組的配置
任意一台服務器上查看鏡像 rocketmq 的組成,分析其中用戶與組的信息:
$ docker history foxiswho/rocketmq:4.7.0
IMAGE CREATED CREATED BY SIZE
1cf46e8f03d0 7 months ago /bin/sh -c #(nop) WORKDIR /home/rocketmq/roc… 0B
<missing> 7 months ago /bin/sh -c #(nop) USER rocketmq 0B
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 1.92kB
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 0B
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 11.3kB
<missing> 7 months ago /bin/sh -c #(nop) EXPOSE 10909 10911 10912 0B
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 10.1kB
<missing> 7 months ago /bin/sh -c #(nop) EXPOSE 9876 0B
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 15.1MB
<missing> 7 months ago /bin/sh -c #(nop) COPY dir:bdc4a8518539da6ce… 11.4kB
<missing> 7 months ago |5 gid=3000 group=rocketmq uid=3000 user=roc… 15.1MB
<missing> 7 months ago /bin/sh -c #(nop) WORKDIR /home/rocketmq/roc… 0B
<missing> 7 months ago /bin/sh -c #(nop) ENV ROCKETMQ_HOME=/home/r… 0B
<missing> 7 months ago /bin/sh -c #(nop) ENV ROCKETMQ_VERSION=4.7.0 0B
<missing> 7 months ago /bin/sh -c #(nop) ARG version=4.7.0 0B
<missing> 7 months ago |4 gid=3000 group=rocketmq uid=3000 user=roc… 1.07MB
<missing> 7 months ago /bin/sh -c #(nop) ARG gid=3000 0B
<missing> 7 months ago /bin/sh -c #(nop) ARG uid=3000 0B
<missing> 7 months ago /bin/sh -c #(nop) ARG group=rocketmq 0B
<missing> 7 months ago /bin/sh -c #(nop) ARG user=rocketmq 0B
<missing> 7 months ago /bin/sh -c yum install -y java-1.8.0-openjdk… 264MB
<missing> 11 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 11 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 11 months ago /bin/sh -c #(nop) ADD file:45a381049c52b5664… 203MB
可以觀察到:
- 組名:rocketmq,組ID:3000
- 用戶名:rocketmq,用戶ID:3000
6、更改目錄歸屬組與用戶與容器的配置一致
兩台服務器上分別創建組與用戶:
## 創建組
$ groupadd rocketmq
## 增加用戶並加入組
$ useradd -g rocketmq rocketmq
## 設置用戶密碼
$ passwd rocketmq
## 更改組的 gid
$ groupmod -g 3000 rocketmq
## 更改用戶的 uid
$ usermod -u 3000 rocketmq
## 查看是否更改成功
$ id rocketmq
三台服務器上分別更改上面創建的目錄的權限為上面創建的組與用戶:
$ chown -R rocketmq:rocketmq /usr/local/rocketmq
7、安裝 RocketMQ NameServer
三台服務器上分別部署 NameServer,操作如下:
$ docker run -d --name rmqnamesr --net host \
-v /usr/local/rocketmq/server/logs:/home/rocketmq/logs \
-e "JAVA_OPT_EXT=-Xms512M -Xmx512M -Xmn128m" \
-p 9876:9876 \
--restart=always \
foxiswho/rocketmq:4.7.0 \
sh mqnamesrv
8、安裝 RocketMQ Broker
三台服務器分別部署 RocketMQ Broker,操作如下:
$ docker run -d --name rmqbroker --net host \
-e "JAVA_OPT_EXT=-Xmx4096m -Xms4096m -Xmn2048m" \
-v /usr/local/rocketmq/broker/logs:/home/rocketmq/logs \
-v /usr/local/rocketmq/broker/store:/home/rocketmq/store \
-v /usr/local/rocketmq/broker/conf:/home/rocketmq/conf \
-p 30909:30909 -p 30911:30911 -p 30912:30912 -p 40911:40911 \
--restart=always \
foxiswho/rocketmq:4.7.0 \
sh mqbroker -c /home/rocketmq/conf/broker.conf
9、部署控制台
服務器一部署 RocketMQ 控制台:
$ docker run -d --name rmqconsole \
-p 8080:8080 \
--restart=always \
-e "JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.2.21:9876;192.168.2.22:9876;192.168.2.23:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false" \
apacherocketmq/rocketmq-console:2.0.0
10、訪問控制台
輸入 http://192.168.2.21:8080 訪問在服務器一部署的 RocketMQ 控制台,進入到如下界面:
然后我們可以通過該控制台進行發送消息來驗證 RocketMQ 是否正常,這里就交由大家自行驗證了,文檔到此結束。