引言

继 [[【RocketMq】NameServ启动脚本剖析(Ver4.9.4)]] 之后又来看看Broker的脚本。总体上来看大差不差,以浏览外围的配置局部调优为主。

mqbroker

#!/bin/sh  if [ -z "$ROCKETMQ_HOME" ] ; then    ## resolve links - $0 may be a link to maven's home    PRG="$0"      # need this for relative symlinks    while [ -h "$PRG" ] ; do      ls=`ls -ld "$PRG"`      link=`expr "$ls" : '.*-> \(.*\)$'`      if expr "$link" : '/.*' > /dev/null; then        PRG="$link"      else        PRG="`dirname "$PRG"`/$link"      fi    done    saveddir=`pwd`      ROCKETMQ_HOME=`dirname "$PRG"`/..      # make it fully qualified    ROCKETMQ_HOME=`cd "$ROCKETMQ_HOME" && pwd`      cd "$saveddir"  fi    export ROCKETMQ_HOME    sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@

后面的一大段脚本的最终目标就是获取ROCKETMQ_HOME的变量。

咱们关注最初一个脚本,这里调用了runbroker.sh的脚本:

sh ${ROCKETMQ_HOME}/bin/runbroker.sh org.apache.rocketmq.broker.BrokerStartup $@

runbroker.sh

runbroker.sh的脚本尽管内容很多,然而大部分和之前剖析NameServ的启动内容是重合的,这里间接跳过其余函数判断,只关注JVM的参数设置局部。

#!/bin/sh  #===========================================================================================  # Java Environment Setting  #===========================================================================================  error_exit ()  {      echo "ERROR: $1 !!"    exit 1  }    [ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=$HOME/jdk/java  [ ! -e "$JAVA_HOME/bin/java" ] && JAVA_HOME=/usr/java  [ ! -e "$JAVA_HOME/bin/java" ] && error_exit "Please set the JAVA_HOME variable in your environment, We need java(x64)!"    export JAVA_HOME  export JAVA="$JAVA_HOME/bin/java"  export BASE_DIR=$(dirname $0)/..  export CLASSPATH=.:${BASE_DIR}/conf:${BASE_DIR}/lib/*:${CLASSPATH}    #===========================================================================================  # JVM Configuration  #===========================================================================================  # The RAMDisk initializing size in MB on Darwin OS for gc-log  DIR_SIZE_IN_MB=600    choose_gc_log_directory()  {      case "`uname`" in          Darwin)              if [ ! -d "/Volumes/RAMDisk" ]; then                  # create ram disk on Darwin systems as gc-log directory                  DEV=`hdiutil attach -nomount ram://$((2 * 1024 * DIR_SIZE_IN_MB))` > /dev/null                  diskutil eraseVolume HFS+ RAMDisk ${DEV} > /dev/null                  echo "Create RAMDisk /Volumes/RAMDisk for gc logging on Darwin OS."              fi              GC_LOG_DIR="/Volumes/RAMDisk"          ;;          *)              # check if /dev/shm exists on other systems              if [ -d "/dev/shm" ]; then                  GC_LOG_DIR="/dev/shm"              else                  GC_LOG_DIR=${BASE_DIR}              fi          ;;    esac}    choose_gc_options()  {      JAVA_MAJOR_VERSION=$("$JAVA" -version 2>&1 | head -1 | cut -d'"' -f2 | sed 's/^1\.//' | cut -d'.' -f1)      if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "8" ] ; then        JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC"    else        JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"    fi        if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "9" ] ; then        JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"        JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"    else        JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"      JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log:time,tags:filecount=5,filesize=30M"      fi  }    choose_gc_log_directory    JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g"choose_gc_options  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} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"  JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"  JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"    numactl --interleave=all pwd > /dev/null 2>&1  if [ $? -eq 0 ]  then   if [ -z "$RMQ_NUMA_NODE" ] ; then    numactl --interleave=all $JAVA ${JAVA_OPT} $@   else    numactl --cpunodebind=$RMQ_NUMA_NODE --membind=$RMQ_NUMA_NODE $JAVA ${JAVA_OPT} $@   fi  else   $JAVA ${JAVA_OPT} $@  fi

choose_gc_options() 剖析

先来看如何抉择GC参数局部。

choose_gc_options()  {      JAVA_MAJOR_VERSION=$("$JAVA" -version 2>&1 | head -1 | cut -d'"' -f2 | sed 's/^1\.//' | cut -d'.' -f1)      if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "8" ] ;     then        JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC"       else        JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"        fi        if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "9" ] ; then        JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"        JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"        else        JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"            JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log:time,tags:filecount=5,filesize=30M"      fi  }

粗略看去和NameServ的参数基本上没啥差异(难堪....),这里只好列举一些这些参数的作用了:

留神Broker对于小于JDK8的版本和小于JDK9的版本做了两种策略,这里的脚本其实是有点奇怪的,因为RocketMq最低不是只反对JDK8么? 当然这样的脚本设置也不是不能够,只是没啥作用罢了。

因为怎么看怎么顺当,为了不便了解,集体调整了一下这个脚本的“实在用意”,针对JDK8和JDK8以下和JDK8以上三个分支判断:

  1. JDK8 以下用CMS+ParNew垃圾收集器经典组合
  2. G1 是在JDK9 才成为默认垃圾收集器的,JDK8 须要手动设置应用G1。须要留神这个版本G1是残血版本,Full Gc是单线程的,JDK11 才被Oracle官网加上去。(顺带一提满血的G1从配置能够猜到是大量复用CMS的代码实现的)
  3. JDK8之前的日志打印参数应用了 xloggc,JDK9以及之后的版本用一个对立的打印参数xlog替换与之配合的附加参数,脚本洁净了很多。
  4. JDK版本晋升能够看出官网在尽可能各方面简化垃圾收集器的参数管制,比方日志接口对立和简化。
choose_gc_options(){    JAVA_MAJOR_VERSION=$("$JAVA" -version 2>&1 | head -1 | cut -d'"' -f2 | sed 's/^1\.//' | cut -d'.' -f1)    # 如果以后版本小于JDK1.8,应用CMS+ParNew垃圾收集器组合和相干参数    if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "8" ] ; then      # CMS + ParNew 垃圾收集器      # CMSInitiatingOccupancyFraction=70 示意当老年代达到70%时,触发CMS垃圾回收。      #  CMSParallelRemarkEnabled 老年代收集器指定为CMS的时候无效,在进行了Full GC时对老年代进行压缩整顿,解决掉内存碎片。      # UseConcMarkSweepGC 应用CMS老年代收集器      # SoftRefLRUPolicyMSPerMB 软援用不给任何的存活工夫,对于序列化或者反射的对象在垃圾回收的时候踊跃清理      # CMSClassUnloadingEnabled 启用对Perm区启用类回收,避免Perm区内存垃圾对象堆满      # -XX:SurvivorRatio=8 Eden 区域在新生代占比,Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10      # -XX:-UseParNewGC ParNew 新生代垃圾收集器       JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC"       # 垃圾收集器日志存储配置       # PrintGCApplicationStoppedTime 打印利用因为GC而产生的进展工夫       # 这个参数的次要作用是能够在JVM运行的时候动静调整新生代的Eden、From、To三个区域的区域调配,计算根据是 GC 过程中统计的 **GC 工夫、吞吐量、内存占用量**。       # -verbose:gc 和 -XX:+PrintGCDetails 垃圾收集时的信息打印 打印开启,大部分时候会一起配置       # PrintGCDateStamps 打印GC产生时的工夫戳,搭配 -XX:+PrintGCDetails 应用,不能够独立应用       # PrintAdaptiveSizePolicy 动静调整 Eden From To 三个区域的大小,判断根据为 GC 工夫、吞吐量、内存占用量       JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"       # 给予5个GC日志文件,每个文件30M,如果5个文件写满,则从第一个文件笼罩。      JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"    # 如果以后版本等于 JDK 1.8    else if [ "$JAVA_MAJOR_VERSION" -eq "8" ]; then      # PrintGCApplicationStoppedTime 打印利用因为GC而产生的进展工夫       # 这个参数的次要作用是能够在JVM运行的时候动静调整新生代的Eden、From、To三个区域的区域调配,计算根据是 GC 过程中统计的 **GC 工夫、吞吐量、内存占用量**。       # -verbose:gc 和 -XX:+PrintGCDetails 垃圾收集时的信息打印 打印开启,大部分时候会一起配置       # PrintGCDateStamps 打印GC产生时的工夫戳,搭配 -XX:+PrintGCDetails 应用,不能够独立应用       # PrintAdaptiveSizePolicy 动静调整 Eden From To 三个区域的大小,判断根据为 GC 工夫、吞吐量、内存占用量      JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"     # 给予5个GC日志文件,每个文件30M,如果5个文件写满,则从第一个文件笼罩。      JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"      # 触发全局并发标记的老年代应用占比,默认值45%。      # UseG1GC G1 垃圾收集器      # SoftRefLRUPolicyMSPerMB 软援用不给任何的存活工夫,对于序列化或者反射的对象在垃圾回收的时候踊跃清理。      # G1HeapRegionSize 16M 一个Region的大小能够通过参数`-XX:G1HeapRegionSize`设定,取值范畴从1M到32M,且是2的指数。如果不设定,那么G1会依据Heap大小主动决定。      JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"    fi            # 如果是JDK 9以及JDK9之后的版本    else      JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"      # -Xlog 是JDK9 对立日志参数,对于之前版本凌乱的GC LOG日志治理进行一波优化      JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log:time,tags:filecount=5,filesize=30M"    fi}

如果一个参数调研那么文章会没完没了,咱们间接拆分三局部进行浏览。

JDK8以下的版本(无用)

实践上来说没屁用的GC参数,因为RocketMq规定了最低反对的JDK版本为JDK1.8。

if [ -z "$JAVA_MAJOR_VERSION" ] || [ "$JAVA_MAJOR_VERSION" -lt "8" ] ; then      # CMS + ParNew 垃圾收集器  # CMSInitiatingOccupancyFraction=70 示意当老年代达到70%时,触发CMS垃圾回收。  #  CMSParallelRemarkEnabled 老年代收集器指定为CMS的时候无效,在进行了Full GC时对老年代进行压缩整顿,解决掉内存碎片。  # UseConcMarkSweepGC 应用CMS老年代收集器  # SoftRefLRUPolicyMSPerMB 软援用不给任何的存活工夫,对于序列化或者反射的对象在垃圾回收的时候踊跃清理  # CMSClassUnloadingEnabled 启用对Perm区启用类回收,避免Perm区内存垃圾对象堆满  # -XX:SurvivorRatio=8 Eden 区域在新生代占比,Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10  # -XX:-UseParNewGC ParNew 新生代垃圾收集器   JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC"   # 垃圾收集器日志存储配置   # PrintGCApplicationStoppedTime 打印利用因为GC而产生的进展工夫   # 这个参数的次要作用是能够在JVM运行的时候动静调整新生代的Eden、From、To三个区域的区域调配,计算根据是 GC 过程中统计的 **GC 工夫、吞吐量、内存占用量**。   # -verbose:gc 和 -XX:+PrintGCDetails 垃圾收集时的信息打印 打印开启,大部分时候会一起配置   # PrintGCDateStamps 打印GC产生时的工夫戳,搭配 -XX:+PrintGCDetails 应用,不能够独立应用   # PrintAdaptiveSizePolicy 动静调整 Eden From To 三个区域的大小,判断根据为 GC 工夫、吞吐量、内存占用量   JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"   # 给予5个GC日志文件,每个文件30M,如果5个文件写满,则从第一个文件笼罩。  JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"

JDK8 版本

从JDK8的版本开始,RocketMq的垃圾收集器变更为G1,对应的参数配置也是G1的配置。然而须要留神-Xloggc的配置文件利用了工夫戳进行格式化避免轮循反复笼罩的问题。其余参数曾经在NameServ的笔记中进行过剖析,集体把参数写入到命令上方不便查看

    # 如果以后版本等于 JDK 1.8    else if [ "$JAVA_MAJOR_VERSION" -eq "8" ]; then              # PrintGCApplicationStoppedTime 打印利用因为GC而产生的进展工夫       # 这个参数的次要作用是能够在JVM运行的时候动静调整新生代的Eden、From、To三个区域的区域调配,计算根据是 GC 过程中统计的 **GC 工夫、吞吐量、内存占用量**。       # -verbose:gc 和 -XX:+PrintGCDetails 垃圾收集时的信息打印 打印开启,大部分时候会一起配置       # PrintGCDateStamps 打印GC产生时的工夫戳,搭配 -XX:+PrintGCDetails 应用,不能够独立应用       # PrintAdaptiveSizePolicy 动静调整 Eden From To 三个区域的大小,判断根据为 GC 工夫、吞吐量、内存占用量      JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"                # 给予5个GC日志文件,每个文件30M,如果5个文件写满,则从第一个文件笼罩。      JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"                 # 触发全局并发标记的老年代应用占比,默认值45%。      # UseG1GC G1 垃圾收集器      # SoftRefLRUPolicyMSPerMB 软援用不给任何的存活工夫,对于序列化或者反射的对象在垃圾回收的时候踊跃清理。      JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"    fi

JDK9 及之后的版本

如果是之后的版本,则基本上的GC垃圾收集器和参数不变,然而须要留神JDK9之后因为Xloggc的参数被废除,用了-xlog的参数作为代替,这个起名的确比拟坑,因为和之前长的特地像。

# 如果是JDK 9以及JDK9之后的版本else  JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"  # -Xlog 是JDK9 对立日志参数,对于之前版本凌乱的GC LOG日志治理进行一波优化  JAVA_OPT="${JAVA_OPT} -Xlog:gc*:file=${GC_LOG_DIR}/rmq_srv_gc_%p_%t.log:time,tags:filecount=5,filesize=30M"fi

Oracle官网文档也解释了这几个JDK8以及之前的日志打印参数被废除。

https://docs.oracle.com/en/java/javase/11/jrockit-hotspot/logging.html#GUID-33074D03-B4F3-4D16-B9B6-8B0076661AAF

须要留神

因为日志参数打印属于JVM 的领域,本节不做过多探讨。

总结

  1. JDK8之前应用Cms+ParNew,JDK8以及之后的版本全副采纳G1垃圾收集器。
  2. NameServ的启动脚本和Broker的相似,看懂任意一个就可以看懂另一个。
  3. Xlog 和 Xloggc 是比拟容易混同的中央,也是集体认为Broker启动脚本在不同版本判断启动参数理论最大的区别。
  4. 按照脚本的判断逻辑,上面的JVM参数在JDK 9及之后会呈现两次。
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0"

写在最初

从脚本格调能够看出和改写NameServ.sh的人是同一个编写,所以有很多大量重复性的内容都给省略了,具体的介绍都放到了nameserv.sh的脚本剖析当中。

通篇看下来集体不太了解为什么要针对JDK8以前的版本做JVM参数调优,或者这就是工程师编写的谨严之处吧,思考全面,值得学习。