1. 程式人生 > 實用技巧 >記一次 RocketMQ broker 因記憶體不足導致的啟動失敗

記一次 RocketMQ broker 因記憶體不足導致的啟動失敗

原創:西狩

編寫日期 / 修訂日期:2020-01-12 / 2020-01-12

版權宣告:本文為博主原創文章,遵循 CC BY-SA-4.0 版權協議,轉載請附上原文出處連結和本宣告。

背景

該小節交代問題發生的背景,急需解決問題的小夥伴,可以跳過本節,直接看下一小節。

因為專案提測,需要搭建一套測試環境。所以呢,是時候展示真正的技術啦!在搞定了容器、中介軟體、專案映象後,小西登入系統對各大模組的功能進行測試。事情到了這裡,小西本來應該會就這樣愉快地完成了部署任務,可是生活總是會給你帶來意想不到的“驚喜”。

  • 在測試一類預警事件訊息時,忽然發現壓根沒有訊息,就去 RocketMQ 的控制檯介面檢視,發現控制檯原本應該乖乖被監控的 broker 一個都不在了。

  • 在不考慮 broker 不會自己罷工跑掉的情況下,登入伺服器檢視 broker 服務,發現服務沒有啟動成功。

  • 再檢視 broker 的啟動日誌,發現啟動報錯了。

於是,就有了這篇分享。

部署環境

作業系統:centos7 linux 系統

部署方式:docker 容器 + docker-compose 容器編排

部署版本:RocketMQ 4.4.0

問題描述

開發環境訪問 RocketMQ 控制檯,發現 broker 服務宕機。登入伺服器檢視日誌發現以下報錯:

Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000c0000000, 7163871232, 0) failed; error=
 ...
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 7163871232 bytes for Failed to commit area from 0x00000000c0000000 to
 ...

提示記憶體分配無法滿足 7163871232 位元組的需求。那為什麼會出現這個問題呢?

問題定位

重啟broker

剛開始沒有排查日誌時,以為環境被人停掉了,所以對 broker 進行了重啟。

[root@172-30-1-135 nginx]# docker-compose restart

發現 broker 啟動依舊失敗,而 namesrv 和 console 啟動正常。

分析啟動指令碼

登入 RocketMQ 的 docker 容器。

注意,因為 broker 無法啟動,使用 docker exec 是無法進入容器的,需要使用 docker run 命令進入容器。

[root@37-128-28-177 nginx]# docker run -it rocketmqinc/rocketmq:4.4.0 bash

檢視啟動指令碼 broker.sh

[rocketmq@38bc66dd72c3 bin]$ vi runbroker.sh

發現 runbroker.sh 啟動指令碼中有最大允許堆記憶體的配置項 MAX_POSSIBLE_HEAP

...
# Get the max heap used by a jvm, which used all the ram available to the container.
if [ -z "$MAX_POSSIBLE_HEAP" ]
then
        MAX_POSSIBLE_RAM_STR=$(java -XX:+UnlockExperimentalVMOptions -XX:MaxRAMFraction=1 -XshowSettings:vm -version |& awk '/Max\. Heap Size \(Estimated\): [0-9KMG]+/{ print $5}')
        MAX_POSSIBLE_RAM=$MAX_POSSIBLE_RAM_STR
        CAL_UNIT=${MAX_POSSIBLE_RAM_STR: -1}
        if [ "$CAL_UNIT" == "G" -o "$CAL_UNIT" == "g" ]; then
                MAX_POSSIBLE_RAM=$(echo ${MAX_POSSIBLE_RAM_STR:0:${#MAX_POSSIBLE_RAM_STR}-1} `expr 1 \* 1024 \* 1024 \* 1024` | awk '{printf "%d",$1*$2}')
        elif [ "$CAL_UNIT" == "M" -o "$CAL_UNIT" == "m" ]; then
                MAX_POSSIBLE_RAM=$(echo ${MAX_POSSIBLE_RAM_STR:0:${#MAX_POSSIBLE_RAM_STR}-1} `expr 1 \* 1024 \* 1024` | awk '{printf "%d",$1*$2}')
        elif [ "$CAL_UNIT" == "K" -o "$CAL_UNIT" == "k" ]; then
                MAX_POSSIBLE_RAM=$(echo ${MAX_POSSIBLE_RAM_STR:0:${#MAX_POSSIBLE_RAM_STR}-1} `expr 1 \* 1024` | awk '{printf "%d",$1*$2}')
        fi
        MAX_POSSIBLE_HEAP=$[MAX_POSSIBLE_RAM/4]
fi

# Dynamically calculate parameters, for reference.
Xms=$MAX_POSSIBLE_HEAP
Xmx=$MAX_POSSIBLE_HEAP
Xmn=$[MAX_POSSIBLE_HEAP/2]
...

從指令碼中可以看出,在 runborker.sh 指令碼中, MAX_POSSIBLE_HEAP 引數值會通過引數進行設定,而如果沒有任何設定就會走下面這個判斷:

MAX_POSSIBLE_HEAP=$[MAX_POSSIBLE_RAM/4]

也就是說 MAX_POSSIBLE_HEAP 引數如果沒有指定,它會使用四分之一的最大可用記憶體 MAX_POSSIBLE_RAM ,這一機制可以保護伺服器的作業系統不會因為被服務佔據全部記憶體而無法正常執行。但當伺服器的可用記憶體較小時,這個四分之一對於 RocketMQ 來說就有些“捉襟見肘”了。所以,也就導致了 RocketMQ 因記憶體不足而無法啟動。

分析出原因以後,就可以考慮通過顯式指定引數的方式解決這個問題。

解決方案

方案一:修改最大堆記憶體

退出 docker 容器,修改 RocketMQ 服務 docker-compose.yml 檔案,給 broker 指定 MAX_POSSIBLE_HEAP 引數,指定為 1024m

broker:
    image: rocketmqinc/rocketmq:4.4.0
    container_name: rmqbroker
    ports:
      - 10909:10909
      - 10911:10911
      - 10912:10912
    volumes:
      - /data/admin/app/yunying/mq/logs/broker:/home/rocketmq/logs
      - /data/admin/app/yunying/mq/broker:/home/rocketmq/store
      - /data/admin/app/yunying/mq/broker.conf:/opt/rocketmq-4.4.0/conf/broker.conf
    command: sh  mqbroker -n 172.30.1.135:9876 -c /opt/rocketmq-4.4.0/conf/broker.conf
    depends_on:
      - namesrv
    environment:
      - "autoCreateTopicEnable=true"
      - "JAVA_HOME=/usr/lib/jvm/jre"
      # 指定堆記憶體大小
      - "MAX_POSSIBLE_HEAP=1024m"
      - TZ=Asia/Shanghai

重啟 broker。檢視日誌,發現以下報錯。

/opt/rocketmq-4.4.0/bin/runbroker.sh: line 58: 1024m: value too great for base (error token is "1024m")

由於原始問題報錯資訊中的單位是 bytes,考慮到引數單位可能與 JVM 記憶體設定引數不同,再次修改堆記憶體配置。

# 省略其他無關資訊
broker:
    environment:
      # 指定堆記憶體大小
      - "MAX_POSSIBLE_HEAP=1073741824"

重啟 broker,啟動成功。

[[email protected] mq]$ docker logs -f --tail 10 rmqbroker
The broker[broker-a, 172.30.1.135:10911] boot success. serializeType=JSON and name server is 172.30.1.135:9876

至此,問題解決。

方案二:修改JVM元空間大小

本方案是網上查詢資料發現的解決方案,報錯問題類似但不完全一致。該方案沒有做驗證,不確定是否能夠解決該問題。

感興趣的小夥伴可以驗證一下,下面是問題描述和解決方案。

問題描述為:

JRE version: (8.0_172-b11) (build )
Java VM: Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode linux-amd64 compressed oops)
Java執行時環境的記憶體不足,無法繼續,本機記憶體分配(mmap)未能對映8589934592位元組,用於提交保留記憶體

解決方案如下:

找到 runserver.sh 和 runbroker.sh,編輯

JAVA_OPT=”${JAVA_OPT} -server -Xms256m -Xmx1024m -Xmn125m -XX:MetaspaceSize=1024m -XX:MaxMetaspaceSize=1024m”

參考資料