31 RocketMQ生產(chǎn)集群準備:進行OS內核參數(shù)和JVM參數(shù)的調整

RocketMQ生產(chǎn)集群準備:進行OS內核參數(shù)和JVM參數(shù)的調整
1、壓測前的準備工作
小猛在折騰了幾天,用公司分配的幾臺機器部署好一個小規(guī)模的RocketMQ集群之后,終于開始思考如何進行壓測的問題了。小猛盯著下面的架構圖,陷入了一陣沉思,像RocketMQ這種中間件集群,應該如何進行壓測,要準備哪些東西呢?
? ? ? ? ? ?

? ? ? ? ? ? ?
如果是一些經(jīng)驗不豐富的平時主要是做CRUD類的增刪改查工作的Java工程師,可能此時就會直接嘗試運行幾個生產(chǎn)者和消費者的程序,然后多開一些線程寫數(shù)據(jù)到RocketMQ,同時從RocketMQ消費數(shù)據(jù)
接著從RocketMQ的管理工作臺中看一下TPS,每秒可以處理多少條消息,此時就算是完成壓測了。
但是小猛并沒有這樣輕舉妄動,他還是選擇去咨詢了一下明哥,提出了這個疑問,壓測一個中間件真的就如此簡單嗎?
明哥聽到小猛這個疑問后,就給小猛解釋起了這種中間件集群的壓測準備工作了。對于生產(chǎn)環(huán)境的中間件集群,不能直接用各種默認參數(shù)啟動,因為那樣可能有很多問題,或者沒法把中間件的性能發(fā)揮出來。
對于一個中間件而言,第一步,你需要對他部署的機器的OS內核參數(shù)進行一定的調整(也就是linux操作系統(tǒng)的一些內核參數(shù))
因為OS內核參數(shù)很多默認值未必適合生產(chǎn)環(huán)境的系統(tǒng)運行,有些參數(shù)的值需要調整大一些,才能讓中間件發(fā)揮出來性能,我們看下面的的圖。
? ? ? ?

? ? ??
接著下一步需要思考的一個問題,就是一般中間件,比如RocketMQ、MyCat、Elasticsearch、Kafka之類的東西,很多都是Java開發(fā)的,或者是基于JVM的Scala開發(fā)的(比如Kafka)
所以你可以認為在一臺機器上部署和啟動一個中間件系統(tǒng),說白了就是啟動一個JVM進程,由這個JVM進程來運行中間件系統(tǒng)內的所有代碼,然后實現(xiàn)中間件系統(tǒng)的各種功能。
我們看下面的圖,這里清晰的展示出了一臺機器上部署了一個中間件系統(tǒng),就是啟動了一個JVM進程的概念。
這里如果有朋友對JVM這塊知識不太了解,給大家推薦一下貍貓技術窩上的《從0開始帶你成為JVM實戰(zhàn)高手》這個專欄,是我的好朋友救火隊隊長寫的,已經(jīng)更新完畢。
? ? ? ? ? ?

?? ? ? ? ? ?
所以其實對于一個生產(chǎn)環(huán)境的中間件系統(tǒng)而言,在部署和啟動之前,需要關注的第二個東西就是JVM的各種參數(shù)
比如內存區(qū)域的大小分配,垃圾回收器以及對應的行為參數(shù),GC日志存放地址,OOM自動導出內存快照的配置,等等。
所以你就需要對JVM進行合理的優(yōu)化配置,比如最簡單的一點,明明你部署了一個幾十GB內存的高配置物理機,結果你就給中間件系統(tǒng)的JVM分配了1GB的內存,你覺得這是不是在開玩笑?
相當于你機器配置很高,結果你的中間件系統(tǒng)就用了里面的幾十分之一的內存,根本沒用上那么多的資源!
我們看下面的圖畫圈的地方,這里就引申出了要配置JVM參數(shù)的概念,這是很多人都會忽略的一點。
? ? ? ? ? ?

?? ? ? ? ? ?
最后第三件事情,就是中間件系統(tǒng)自己本身的一些核心參數(shù)的設置,比如你的中間件系統(tǒng)會開啟很多線程處理請求和工作負載,然后還會進行大量的網(wǎng)絡通信,同時會進行大量的磁盤IO類的操作。
這個時候你就需要依據(jù)你的機器配置,合理的對中間件系統(tǒng)的核心參數(shù)進行調整
比如你的機器配置很高,是24核CPU,結果你的中間件系統(tǒng)默認就開啟了4個工作線程去處理請求,這不是在開玩笑么!相當于24核CPU里很多都是空閑狀態(tài),是沒有任何事情可以干的。
要是不進行合理的參數(shù)設置,幾乎可以認為就是在浪費高配置的機器資源!
所以以上三點,就是對任何一個中間件系統(tǒng),在進行壓力測試以及生產(chǎn)環(huán)境部署之前,都必須要進行調整的!
當然如果是普通的那種Java Web業(yè)務系統(tǒng),通常而言上線之前主要關注的就是JVM的參數(shù)而已,對os內核參數(shù)以及業(yè)務系統(tǒng)自身參數(shù)大多數(shù)情況下都沒有太多的要求
但是中間件系統(tǒng)而言,往往必須要對os內核參數(shù)、jvm參數(shù)以及自身核心參數(shù)都做出相對應的合理的調整,再進行壓測和上線。
2、對RocketMQ集群進行OS內核參數(shù)的調整
接著明哥就開始為小猛講解RocketMQ集群部署的機器需要調整的一些os內核參數(shù)的含義,并且給小猛說了一些他建議的調整值。
(1)vm.overcommit_memory
“vm.overcommit_memory”這個參數(shù)有三個值可以選擇,0、1、2。
如果值是0的話,在你的中間件系統(tǒng)申請內存的時候,os內核會檢查可用內存是否足夠,如果足夠的話就分配內存給你,如果感覺剩余內存不是太夠了,干脆就拒絕你的申請,導致你申請內存失敗,進而導致中間件系統(tǒng)異常出錯。
因此一般需要將這個參數(shù)的值調整為1,意思是把所有可用的物理內存都允許分配給你,只要有內存就給你來用,這樣可以避免申請內存失敗的問題。
比如我們曾經(jīng)線上環(huán)境部署的Redis就因為這個參數(shù)是0,導致在save數(shù)據(jù)快照到磁盤文件的時候,需要申請大內存的時候被拒絕了,進而導致了異常報錯。
可以用如下命令修改:echo?'vm.overcommit_memory=1'?>>?/etc/sysctl.conf。
(2)vm.max_map_count
這個參數(shù)的值會影響中間件系統(tǒng)可以開啟的線程的數(shù)量,同樣也是非常重要的
如果這個參數(shù)過小,有的時候可能會導致有些中間件無法開啟足夠的線程,進而導致報錯,甚至中間件系統(tǒng)掛掉。
他的默認值是65536,但是這個值有時候是不夠的,比如我們大數(shù)據(jù)團隊的生產(chǎn)環(huán)境部署的Kafka集群曾經(jīng)有一次就報出過這個異常,說無法開啟足夠多的線程,直接導致Kafka宕機了。
因此建議可以把這個參數(shù)調大10倍,比如655360這樣的值,保證中間件可以開啟足夠多的線程。
可以用如下命令修改:echo?'vm.max_map_count=655360'?>>?/etc/sysctl.conf。
(3)vm.swappiness
這個參數(shù)是用來控制進程的swap行為的,這個簡單來說就是os會把一部分磁盤空間作為swap區(qū)域,然后如果有的進程現(xiàn)在可能不是太活躍,就會被操作系統(tǒng)把進程調整為睡眠狀態(tài),把進程中的數(shù)據(jù)放入磁盤上的swap區(qū)域,然后讓這個進程把原來占用的內存空間騰出來,交給其他活躍運行的進程來使用。
如果這個參數(shù)的值設置為0,意思就是盡量別把任何一個進程放到磁盤swap區(qū)域去,盡量大家都用物理內存。
如果這個參數(shù)的值是100,那么意思就是盡量把一些進程給放到磁盤swap區(qū)域去,內存騰出來給活躍的進程使用。
默認這個參數(shù)的值是60,有點偏高了,可能會導致我們的中間件運行不活躍的時候被迫騰出內存空間然后放磁盤swap區(qū)域去。
因此通常在生產(chǎn)環(huán)境建議把這個參數(shù)調整小一些,比如設置為10,盡量用物理內存,別放磁盤swap區(qū)域去。
可以用如下命令修改:echo?'vm.swappiness=10'?>>?/etc/sysctl.conf。
(4)ulimit
這個是用來控制linux上的最大文件鏈接數(shù)的,默認值可能是1024,一般肯定是不夠的,因為你在大量頻繁的讀寫磁盤文件的時候,或者是進行網(wǎng)絡通信的時候,都會跟這個參數(shù)有關系
對于一個中間件系統(tǒng)而言肯定是不能使用默認值的,如果你采用默認值,很可能在線上會出現(xiàn)如下錯誤:error: too many open files。
因此通常建議用如下命令修改這個值:echo?'ulimit?-n?1000000'?>>?/etc/profile。
(5)一點小小的總結
其實大家綜合思考一下這幾個參數(shù),會發(fā)現(xiàn)到最后要調整的東西,無非都是跟磁盤文件IO、網(wǎng)絡通信、內存管理、線程數(shù)量有關系的,因為我們的中間件系統(tǒng)在運行的時候無非就是跟這些打交道。
中間件系統(tǒng)肯定要開啟大量的線程(跟vm.max_map_count有關)
而且要進行大量的網(wǎng)絡通信和磁盤IO(跟ulimit有關)
然后大量的使用內存(跟vm.swappiness和vm.overcommit_memory有關)
所以對OS內核參數(shù)的調整,往往也就是圍繞跟中間件系統(tǒng)運行最相關的一些東西。
3、對JVM參數(shù)進行調整
接著明哥開始給小猛講解起了RocketMQ的JVM參數(shù)如何調整,首先得先找到RocketMQ啟動腳本中是如何設置JVM參數(shù)的,并且明白默認JVM參數(shù)的含義,同時再有針對性的做出適當?shù)恼{整和修改。
我們回顧一下之前講部署RocketMQ集群的時候,給大家介紹過幾個啟動腳本
在rocketmq/distribution/target/apache-rocketmq/bin目錄下,就有對應的啟動腳本,比如mqbroker是用來啟動Broker的,mqnamesvr是用來啟動NameServer的。
用mqbroker來舉例,我們查看這個腳本里的內容,最后有如下一行:
sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@
這一行內容就是用runbroker.sh腳本來啟動一個JVM進程,JVM進程剛開始執(zhí)行的main類就是org.apache.rocketmq.broker.BrokerStartup
我們接著看runbroker.sh腳本,在里面可以看到如下內容:
JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g -Xmn4g"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch"
JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"
在上面的內容中,其實就是在為啟動Broker設置對應的JVM參數(shù)和其他一些參數(shù),我們可以把其中JVM相關的參數(shù)抽取出來給大家解釋一下:
“-server -Xms8g -Xmx8g -Xmn4g -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m -XX:-OmitStackTraceInFastThrow -XX:+AlwaysPreTouch -XX:MaxDirectMemorySize=15g -XX:-UseLargePages -XX:-UseBiasedLocking”
我們來分門別類解釋一下這些參數(shù),當然解釋這些參數(shù)是建立在大家要對JVM有一定的了解基礎之上,因此還是建議大家先看一下貍貓技術窩的《從0開始帶你成為JVM實戰(zhàn)高手》這個專欄。
-server:這個參數(shù)就是說用服務器模式啟動,這個沒什么可說的,現(xiàn)在一般都是如此
-Xms8g -Xmx8g -Xmn4g:這個就是很關鍵的一塊參數(shù)了,也是重點需要調整的,就是默認的堆大小是8g內存,新生代是4g內存,但是我們的高配物理機是48g內存的
所以這里完全可以給他們翻幾倍,比如給堆內存20g,其中新生代給10g,甚至可以更多一些,當然要留一些內存給操作系統(tǒng)來用
-XX:+UseG1GC -XX:G1HeapRegionSize=16m:這幾個參數(shù)也是至關重要的,這是選用了G1垃圾回收器來做分代回收,對新生代和老年代都是用G1來回收
這里把G1的region大小設置為了16m,這個因為機器內存比較多,所以region大小可以調大一些給到16m,不然用2m的region,會導致region數(shù)量過多的
-XX:G1ReservePercent=25:這個參數(shù)是說,在G1管理的老年代里預留25%的空閑內存,保證新生代對象晉升到老年代的時候有足夠空間,避免老年代內存都滿了,新生代有對象要進入老年代沒有充足內存了
默認值是10%,略微偏少,這里RocketMQ給調大了一些
-XX:InitiatingHeapOccupancyPercent=30:這個參數(shù)是說,當堆內存的使用率達到30%之后就會自動啟動G1的并發(fā)垃圾回收,開始嘗試回收一些垃圾對象
默認值是45%,這里調低了一些,也就是提高了GC的頻率,但是避免了垃圾對象過多,一次垃圾回收耗時過長的問題
-XX:SoftRefLRUPolicyMSPerMB=0:這個參數(shù)默認設置為0了,在JVM優(yōu)化專欄中,救火隊隊長講過這個參數(shù)引發(fā)的案例,其實建議這個參數(shù)不要設置為0,避免頻繁回收一些軟引用的Class對象,這里可以調整為比如1000
-verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m:這一堆參數(shù)都是控制GC日志打印輸出的,確定了gc日志文件的地址,要打印哪些詳細信息,然后控制每個gc日志文件的大小是30m,最多保留5個gc日志文件。
-XX:-OmitStackTraceInFastThrow:這個參數(shù)是說,有時候JVM會拋棄一些異常堆棧信息,因此這個參數(shù)設置之后,就是禁用這個特性,要把完整的異常堆棧信息打印出來
-XX:+AlwaysPreTouch:這個參數(shù)的意思是我們剛開始指定JVM用多少內存,不會真正分配給他,會在實際需要使用的時候再分配給他
所以使用這個參數(shù)之后,就是強制讓JVM啟動的時候直接分配我們指定的內存,不要等到使用內存的時候再分配
-XX:MaxDirectMemorySize=15g:這是說RocketMQ里大量用了NIO中的direct buffer,這里限定了direct buffer最多申請多少,如果你機器內存比較大,可以適當調大這個值,如果有朋友不了解direct buffer是什么,可以自己查閱一些資料。
-XX:-UseLargePages -XX:-UseBiasedLocking:這兩個參數(shù)的意思是禁用大內存頁和偏向鎖,這兩個參數(shù)對應的概念每個要說清楚都得一篇文章,所以這里大家直接知道人家禁用了兩個特性即可。
最后我們做一點小的總結,RocketMQ默認的JVM參數(shù)是采用了G1垃圾回收器,默認堆內存大小是8G
這個其實完全可以根據(jù)大家的機器內存來調整,你可以增大一些也是沒有問題的,然后就是一些G1的垃圾回收的行為參數(shù)做了調整,這個一般我們不用去動,然后就是對GC日志打印做了設置,這個一般也不用動。
其余的就是禁用一些特性,開啟一些特性,這些都直接維持RocketMQ的默認值即可。
4、對RocketMQ核心參數(shù)進行調整
明哥講完RocketMQ默認的JVM參數(shù)以及我們要做的一些微調之后,就繼續(xù)講RocketMQ自身的一些核心參數(shù)的調整了。
之前講解集群部署的時候給大家提過,在下面的目錄里有dledger的示例配置文件:rocketmq/distribution/target/apache-rocketmq/conf/dledger
在這里主要是有一個較為核心的參數(shù):sendMessageThreadPoolNums=16
這個參數(shù)的意思就是RocketMQ內部用來發(fā)送消息的線程池的線程數(shù)量,默認是16
其實這個參數(shù)可以根據(jù)你的機器的CPU核數(shù)進行適當增加,比如機器CPU是24核的,可以增加這個線程數(shù)量到24或者30,都是可以的。
RocketMQ還有一些其他的核心參數(shù),在后續(xù)專欄講解的過程中,咱們再繼續(xù)來分析如何優(yōu)化和調整。
5、今日內容總結
我們最后來簡單對今天講解的內容作一下總結:
(1)中間件系統(tǒng)在壓測或者上生產(chǎn)之前,需要對三大塊參數(shù)進行調整:OS內核參數(shù)、JVM參數(shù)以及中間件核心參數(shù)
(2)OS內核參數(shù)主要調整的地方都是跟磁盤IO、網(wǎng)絡通信、內存管理以及線程管理有關的,需要適當調節(jié)大小
(3)JVM參數(shù)需要我們去中間件系統(tǒng)的啟動腳本中尋找他的默認JVM參數(shù),然后根據(jù)機器的情況,對JVM的堆內存大小,新生代大小,Direct Buffer大小,等等,做出一些調整,發(fā)揮機器的資源
(4)中間件核心參數(shù)主要也是關注其中跟網(wǎng)絡通信、磁盤IO、線程數(shù)量、內存 管理相關的,根據(jù)機器資源,適當可以增加網(wǎng)絡通信線程,控制同步刷磁盤或者異步刷磁盤,線程數(shù)量有多少,內存中一些隊列的大小
End
專欄版權歸公眾號儒猿技術窩所有
未經(jīng)許可不得傳播,如有侵權將追究法律責任