1. 程式人生 > 程式設計 >教你一手如何基於RocketMQ搭建生產級訊息叢集

教你一手如何基於RocketMQ搭建生產級訊息叢集

前言

目前很多網際網路公司的系統都在朝著微服務化、分散式化系統的方向在演進,這帶來了很多好處,也帶來了一些棘手的問題,其中最棘手的莫過於資料一致性問題了。早期我們的軟體功能都在一個程式中,資料的一致性可以通過資料庫本地事務來加以控制。而在分散式架構下,原本比較完整的本地功能可能被拆分成了多個獨立的服務程式。與之前相比,同樣一筆業務訂單此時可能會經歷很多服務模組的處理,呼叫鏈路會變得很長,例如某電商平臺,一筆購物訂單可能會經過:商品中心、訂單、支付、物流等多個服務的呼叫,而這可能還只是比較粗粒度的劃分,某些比較大型的服務,如支付系統,可能本身又會按照分散式的架構拆分成多個微服務,所以整個業務的呼叫鏈路會變得更加冗長。

而這不可避免的就會產生資料不一致的問題,為了實現業務上的最終一致性,功能比較獨立的系統,如訂單系統與支付系統就會通過額外的業務邏輯設計來確保彼此之間的最終一致性,如訂單系統會通過訂單的支付狀態來保持與支付系統的資料一致,而支付系統則會提供支付狀態查詢介面,或者實現最大可能的主動回撥功能,來確保二者資料狀態的最終一致。此外可能還會通過日終的訂單對賬來發現不一致的資料,並進行資料校正。

但是這些都只是業務邏輯上的手段,對於某些內部服務之間的呼叫,如果可以通過分散式事務解決方案來加以保證的話,其實是可以大大減少一些不必要的複雜業務邏輯的。實際上,目前市面上能夠提供分散式事務解決方案、又比較成熟的開源技術框架比較少,而RocketMQ在4.3.0之後的版本提供了事務訊息的功能

,因為RocketMQ本身擁有比較多的生產實踐的關係,所以這一功能備受關注,作者所在的公司也有一些實踐。

以此為契機,為了給大家關於分散式事務一個比較清晰的認識,這裡我打算以RocketMQ的事務訊息功能為示例,來相對全面的總結下分散式事務的內容。本篇文章的主要內容,是先介紹如何搭建一套生產級的RocketMQ訊息叢集,以此準備下試驗環境。

什麼是RocketMQ

RocketMQ是阿里開源的並貢獻給Apache基金會的一款分散式訊息平臺,它具有低延遲、高效能和可靠性、萬億級容量和靈活的可伸縮性的特點,單機也可以支援億級的訊息堆積能力、單機寫入TPS單例項約7萬條/秒,單機部署3個Broker,可以跑到最高12萬條/秒。

基於以上強大的效能,以及阿里的技術影響力,RocketMQ目前在國內網際網路公司中被使用得比較廣泛。那麼,我們先大概來瞭解一下RocketMQ的整體結構吧!

整個RocketMQ訊息系統主要由如下4個部分組成

從中介軟體服務角度來看整個RocketMQ訊息系統(服務端)主要分為:NameSrv和Broker兩個部分。

NameSrv:在RocketMQ分散式訊息系統中,NameSrv主要提供兩個功能:

提供服務發現和註冊,這裡主要是管理Broker,NameSrv接受來自Broker的註冊,並通過心跳機制來檢測Broker服務的健康性;

提供路由功能,叢集(這裡是指以叢集方式部署的NameSrv)中的每個NameSrv都儲存了Broker叢集(這裡是指以叢集方式部署的Broker)中整個的路由資訊和佇列資訊。這裡需要注意,在NameSrv叢集中,每個NameSrv都是相互獨立的,所以每個Broker需要連線所有的NameSrv,每建立一個新的topic都要同步到所有的NameSrv上。

Broker:主要是負責訊息的儲存、傳遞、查詢以及高可用(HA)保證等。其由如下幾個子模組(原始碼總體上也是這麼拆分的)構成:

  • remoting,是Broker的服務入口,負責客戶端的接入(Producer和Consumer)和請求處理。
  • client,管理客戶端和維護消費者對於Topic的訂閱。
  • store,提供針對儲存和訊息查詢的簡單的API(資料儲存在物理磁碟)。
  • HA, 提供資料在主從節點間同步的功能特性。
  • Index,通過特定的key構建訊息索引,並提供快速的索引查詢服務。

而從客戶端的角度看主要有:Producer、Consumer兩個部分。

*Producer:訊息的生產者,由使用者進行分散式部署,訊息由Producer通過多種負載均衡模式傳送到Broker叢集,傳送低延時,支援快速失敗。

Consumer:訊息的消費者,也由使用者部署,支援PUSH和PULL兩種消費模式,支援叢集消費和廣播訊息,提供實時的訊息訂閱機制,滿足大多數消費場景。

來總結下,整個RocketMQ訊息叢集就是由NameSrv/Broker、Producer/Consumer組成的。為了讓大家更清晰的理解它們之間的關係,我們以一條完整的資訊流轉為例,來看看RocketMQ訊息系統是如何運轉的,如下圖所示:

看到這裡相信大家應該對RocketMQ有一個大致的瞭解了,那麼下面我們就具體看看,如何搭建一套生產級的RocketMQ訊息集群系統吧!

RocketMQ叢集模式

RocketMQ叢集部署有多種模式,對於NameSrv來說可以同時部署多個節點,並且這些節點間也不需要有任何的資訊同步,這裡因為每個NameSrv節點都會儲存全量路由資訊,在NameSrv叢集模式下,每個Broker都需要同時向叢集中的每個NameSrv節點傳送註冊資訊,所以這裡對於NameSrv的叢集部署來說並不需要做什麼額外的設定。

而對於Broker叢集來說就有多種模式了,這裡我先給大家介紹下這幾種模式,然後我們再來看看生產級的部署方式是什麼:

1)、單個Master模式

一個Broker作為主服務,不設定任何Slave,很顯然這種方式風險比較大,存在單節點故障會導致整個基於訊息的服務掛掉,所以生產環境不可能採用這種模式。

2)、多Master模式

這種模式的Broker叢集,全是Master,沒有Slave節點。這種方式的優缺點如下:

優點:配置會比較簡單一些,如果單個Master掛掉或重啟維護的話對應用是沒有什麼影響的。如果磁碟配置為RAID10(伺服器的磁碟陣列模式,遺忘的同學可以自己查下資料)的話,即使在機器宕機不可恢復的情況下,由於RAID10磁碟本身的可靠性,訊息也不會丟失(非同步刷盤丟失少量訊息,同步刷盤一條不丟),這種Broker的叢集模式效能相對來說是最高的。

缺點:就是在單臺機器宕機期間,這臺機器上未被消費的訊息在機器恢復之前是不可以進行訊息訂閱的,這對訊息的實時性會有一些影響。

3)、多Master多Slave模式(非同步複製)

在這種模式下Broker叢集存在多個Master節點,並且每個Master節點都會對應一個Slave節點,有多對Master-Slave,HA(高可用)之間採用非同步複製的方式進行資訊同步,在這種方式下主從之間會有短暫的毫秒級的訊息延遲。

優點:在這種模式下即便磁碟損壞了,訊息丟失的情況也非常少,因為主從之間有資訊備份;並且,在這種模式下訊息的實時性也不會受影響,因為Master宕機後Slave可以自動切換為Master模式,這樣Consumer仍然可以通過Slave進行訊息消費,而這個過程對應用來說則是完全透明的,並不需要人工幹預;另外,這種模式的效能與多Master模式幾乎差不多。

缺點:如果Master宕機,並且在磁碟損壞的情況下,會丟失少量的訊息。

4)、多Master多Slave模式(同步複製)

這種模式與3)差不多,只是HA採用的是同步雙寫的方式,即主備都寫成功後,才會嚮應用返回成功。

優點:在這種模式下資料與服務都不存在單點的情況,在Master宕機的情況下,訊息也沒有延遲,服務的可用性以及資料的可用性都非常高。

缺點:效能相比於非同步複製來說略低一些(大約10%);另外一個缺點就是相比於非同步複製,目前Slave備機還暫時不能實現自動切換為Master,可能後續的版本會支援Master-Slave的自動切換功能。

生產級RocketMQ叢集

綜合考慮以上叢集模式的優缺點,在實際生產環境中目前基於RocketMQ訊息叢集的部署方式基本都是採用**多Master多Slave(非同步複製)**這種模式,作者目前所在公司的生產環境的Rocket訊息系統也是採用這種模式進行部署的。

以下為目前作者所在公司的實際部署結構:

在以上實踐中,部署了3個NameSrv節點,Broker採用2主2從的非同步複製模式進行叢集部署。

為了更好地理解RocketMQ的叢集執行原理,接下來我們以4臺虛擬機器器來模擬上述叢集的搭建過程,假設這4臺機器的IP分別為:

10.211.55.4
10.211.55.5
10.211.55.6
10.211.55.7
複製程式碼

首先確保幾臺虛擬機器器上安裝了JDK1.8+:

wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" https://download.oracle.com/otn-pub/java/jdk/8u191-b12/2787e4a523244c269598db4e85c51e0c/jdk-8u191-linux-x64.tar.gz

JAVA_HOME=/opt/java/jdk1.8.0_191
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
複製程式碼

其次我們打算通過RocketMQ的原始碼進行編譯,因為原始碼是基於Maven開發的Java工程,所以我們需要安裝下Maven環境:

wget https://archive.apache.org/dist/maven/binaries/apache-maven-3.2.1-bin.tar.gz

export MAVEN_HOME=/opt/apache-maven-3.2.1
export PATH=${PATH}:${MAVEN_HOME}/bin

#配置阿里雲映象
<mirror>
       <id>nexus-aliyun</id>
       <mirrorOf>central</mirrorOf>
       <name>Nexus aliyun</name>
       <url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
複製程式碼

如果多臺機器,沒有必要依次下載,可以通過遠端複製命令完成機器間的拷貝:

scp -r /opt/apache-maven-3.2.1/ [email protected]:/opt/
複製程式碼

完成後,我們就可以在主機的指定目錄下載RocketMQ的原始碼釋出包(這裡為4.3.2版本)進行編譯了:

#download-4.3.2原始碼準備編譯

wget http://mirror.bit.edu.cn/apache/rocketmq/4.3.2/rocketmq-all-4.3.2-source-release.zip 

mvn -Prelease-all -DskipTests clean install -U

cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq
複製程式碼

以上動作需要在各個機器節點也同步操作!編譯完成後,我們來規劃下如何利用這4臺虛擬機器器來實現“3個NameSrv節點、2組Master-Slave Broker叢集”的效果。

因為本地機器資源原因,我們通過虛擬機器器混部的方式來實現上述叢集的效果,4臺機器的3臺會分別作為NameSrv節點,而對於Broker叢集則兩兩組合,如上表所示!

按照上述規劃,接下來我們就來看下具體的配置方式:

1)Master-Broker-a的配置 建立資料儲存目錄

[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/commitlog
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/consumequeue
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/index
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/checkpoint
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/abort
複製程式碼

切換伺服器目錄為對應的配置檔案目錄

cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf

cd 2m-2s-async //這裡因為我們採用的是非同步複製模式,所以需要編輯2m-2s-async中的配置檔案
複製程式碼

編輯Broker叢集配置檔案 [root@bogon 2m-2s-async]# vim broker-a.properties

#broker所屬哪個叢集,預設【DefaultCluster】
brokerClusterName=DefaultCluster

#broker 實列名稱,主從關係的需要保持名稱一致
brokerName=broker-a

#brokerId,必須是大等於0的整數,0表示Master,>0表示Slave
brokerId=0

#刪除檔案的時間點,預設為凌晨4點
deleteWhen=04

#檔案保留時間,預設為48小時
fileReservedTime=48

#-ASYNC_MASTER 非同步複製Master
#-SYNC_MASTER  同步雙寫Master
#-SLAVE
brokerRole=ASYNC_MASTER

#刷盤方式
#-ASYNC_FLUSH 非同步刷盤
#-SYNC_FLUSH 同步刷盤
flushDiskType=ASYNC_FLUSH

#NameSrv叢集地址
namesrvAddr=10.211.55.4:9876;10.211.55.5:9876;10.211.55.6:9876

#broker對外服務的監聽埠
listenPort=10911

defaultTopicQueueNums=4

#是否允許broker自動建立Topic,建議線下開啟,線上關閉,預設【true】
autoCreateTopicEnable=false

#是否允許broker自動建立訂閱組,建議線下開啟,線上關閉,預設【true】
autoCreateSubscriptionGroup=false
mapedFileSizeCommitLog=1073741824 
mapedFileSizeConsumeQueue=50000000
destroyMapedFileIntervalForcibly=120000
redeleteHangedFileInterval=120000
diskMaxUsedSpaceRatio=88
storePathRootDir=/usr/local/rocketmq/data/store
storePathCommitLog=/usr/local/rocketmq/data/store/commitlog

#消費佇列儲存路徑
storePathConsumeQueue=/usr/local/rocketmq/data/store/consumequeue

#訊息索引儲存路徑
storePathIndex=/usr/local/rocketmq/data/store/index

#checkpoint檔案儲存路徑
storeCheckpoint=/usr/local/rocketmq/data/store/checkpoint

#abort檔案儲存路徑
abortFile=/usr/local/rocketmq/data/store/abort
maxMessageSize=65536
flushCommitLogLeastPages=4
flushConsumeQueueLeastPages=2
flushCommitLogThoroughInterval=10000
flushConsumeQueueThoroughInterval=60000
checkTransactionMessageEnable=false
sendMessageThreadPoolNums=128
pullMessageThreadPoolNums=128
複製程式碼

2)、Slave-Broker-a的配置 建立資料儲存目錄

[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/commitlog
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/consumequeue
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/index
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/checkpoint
[root@bogon local]# mkdir -p /usr/local/rocketmq/data/store/abort
複製程式碼

切換伺服器目錄為對應的配置檔案目錄

cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf

cd 2m-2s-async //這裡因為我們採用的是非同步複製模式,所以需要編輯2m-2s-async中的配置檔案
複製程式碼

3)、Master-Broker-b的配置

參考Master-Broker-a的配置方式,只需要改下“brokerName=broker-b”即可,其他一樣!

4)、Slave-Broker-b的配置

參考Slave-Broker-a的配置,只需要改下“brokerName=broker-b”即可,其他一樣!

至此,我們就完成了整個RocketMQ叢集的配置了!接下來我們啟動整個叢集。

首先我們需要關閉各個節點的防火牆(很重要)

service iptables stop 
#CentOs7
systemctl stop firewalld
複製程式碼

先分別啟動三個NameSrv節點

cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq
nohup sh bin/mqnamesrv &
tail -f ~/logs/rocketmqlogs/namesrv.log
複製程式碼

啟動Broker叢集

#啟動Master broker-a服務(10.211.55.4)
cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/bin/
[root@bogon bin]#
nohup sh mqbroker -c /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf/2m-2s-sync/broker-a.properties >/dev/null 2>&1 &

#啟動Slave broker-a-s服務
cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/bin/
nohup sh mqbroker -c /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf/2m-2s-sync/broker-a-s.properties >/dev/null 2>&1 &

#啟動Master broker-b服務(10.211.55.6)
cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/bin/
nohup sh mqbroker -c /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf/2m-2s-sync/broker-b.properties >/dev/null 2>&1 &

#啟動Slave broker-b-s服務(10.211.55.7)
cd /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/bin
nohup sh mqbroker -c /opt/rocketmq-all-4.3.2/distribution/target/apache-rocketmq/conf/2m-2s-sync/broker-b-s.properties >/dev/null 2>&1 &
複製程式碼

到這裡,我們就完成了RocketMQ生產級叢集的模擬搭建,可以通過jps命令檢查各節點NameSrv&Broker程式是否啟動成功。

叢集啟動成功後,可以通過常用命令來進行檢視!如: 檢視所有消費組group

[root@bogon bin]# sh mqadmin consumerProgress -n 10.211.55.6:9876 
複製程式碼

檢視叢集訊息

[root@bogon bin]# sh mqadmin clusterList  -n 10.211.55.5:9876
複製程式碼