【RocketMq】Broker 启动脚本分析

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【RocketMq】Broker 启动脚本分析

引言

继 [[【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以及之前的日志打印参数被废弃。

docs.oracle.com/en/java/jav…

需要注意

由于日志参数打印属于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参数调优,或许这就是工程师编写的严谨之处吧,考虑全面,值得学习。

相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
5月前
|
物联网
如何在腾讯云等平台搭建自己的物联网MQTT服务器Broker
物联网技术及MQTT协议被广泛应用于各种场景。本文介绍物联网MQTT服务助手下载,如何搭建自己的物联网平台,并使用 “MQTT客户端调试工具”模拟MQTT设备,接入平台进行消息收发。
455 37
|
5月前
|
消息中间件 架构师 Java
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?
|
7月前
|
边缘计算 负载均衡 NoSQL
FreeMQTT Plus: 一个新型 MQTT Broker 集群的实现
FreeMQTT Plus 是一款基于 MQTT 协议的高性能消息中间件,采用分布式架构解决单点瓶颈问题。其核心由 Nginx 负载均衡器、黑(A)节点(MQTT Broker)、白(B)节点(消息路由)和日志(L)节点组成。通过无主从设计,支持高可用性、负载均衡与灵活扩展。针对会话同步、消息路由等挑战,FreeMQTT Plus 利用 MQTT5 特性定义元命令,实现节点间高效通信,无需依赖第三方组件。适用于物联网海量设备接入与高并发场景,为未来边缘计算和多级集群部署提供坚实基础。
1297 74
|
6月前
|
存储 消息中间件 缓存
RocketMQ原理—3.源码设计简单分析下
本文介绍了Producer作为生产者是如何创建出来的、启动时是如何准备好相关资源的、如何从拉取Topic元数据的、如何选择MessageQueue的、与Broker是如何进行网络通信的,Broker收到一条消息后是如何存储的、如何实时更新索引文件的、如何实现同步刷盘以及异步刷盘的、如何清理存储较久的磁盘数据的,Consumer作为消费者是如何创建和启动的、消费者组的多个Consumer会如何分配消息、Consumer会如何从Broker拉取一批消息。
313 11
RocketMQ原理—3.源码设计简单分析下
|
6月前
|
消息中间件 Java 数据管理
RocketMQ原理—2.源码设计简单分析上
本文介绍了NameServer的启动脚本、启动时会解析哪些配置、如何初始化Netty网络服务器、如何启动Netty网络服务器,介绍了Broker启动时是如何初始化配置的、BrokerController的创建以及包含的组件、BrokerController的初始化、启动、Broker如何把自己注册到NameServer上、BrokerOuterAPI是如何发送注册请求的,介绍了NameServer如何处理Broker的注册请求、Broker如何发送定时心跳
|
消息中间件 监控 数据挖掘
基于RabbitMQ与Apache Flink构建实时分析系统
【8月更文第28天】本文将介绍如何利用RabbitMQ作为数据源,结合Apache Flink进行实时数据分析。我们将构建一个简单的实时分析系统,该系统能够接收来自不同来源的数据,对数据进行实时处理,并将结果输出到另一个队列或存储系统中。
994 2
|
消息中间件 存储 数据中心
RocketMQ的长轮询(Long Polling)实现分析
文章深入分析了RocketMQ的长轮询实现机制,长轮询结合了推送(push)和拉取(pull)两种消息消费模式的优点,通过客户端和服务端的配合,确保了消息的实时性同时将主动权保留在客户端。文中首先解释了长轮询的基本概念和实现步骤,然后通过一个简单的实例模拟了长轮询的过程,最后详细介绍了RocketMQ中DefaultMQPushConsumer的长轮询实现方式,包括PullMessage服务、PullMessageProcessor服务和PullCallback回调的工作原理。
447 1
|
消息中间件 SQL 监控
RocketMQ 5.3.0 版本中 Broker IP 配置为 IPv6 的情况
【8月更文第28天】RocketMQ 是一款分布式消息中间件,支持多种消息发布和订阅模式。在 RocketMQ 5.3.0 版本中,Broker 的配置文件 `broker.conf` 允许配置 IPv6 地址。当 Broker 的 `brokerIP1` 配置为 IPv6 地址时,会对 Broker 的启动、消息推送和状态监控等方面产生影响。本文将探讨如何在 RocketMQ 中配置 IPv6 地址,并检查 Broker 的状态。
800 0
|
3月前
|
消息中间件 数据管理 Serverless
阿里云消息队列 Apache RocketMQ 创新论文入选顶会 ACM FSE 2025
阿里云消息团队基于 Apache RocketMQ 构建 Serverless 消息系统,适配多种主流消息协议(如 RabbitMQ、MQTT 和 Kafka),成功解决了传统中间件在可伸缩性、成本及元数据管理等方面的难题,并据此实现 ApsaraMQ 全系列产品 Serverless 化,助力企业提效降本。
|
1月前
|
消息中间件 Java Kafka
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
本文深入解析了 Kafka 和 RabbitMQ 两大主流消息队列在 Spring 微服务中的应用与对比。内容涵盖消息队列的基本原理、Kafka 与 RabbitMQ 的核心概念、各自优势及典型用例,并结合 Spring 生态的集成方式,帮助开发者根据实际需求选择合适的消息中间件,提升系统解耦、可扩展性与可靠性。
163 1
消息队列比较:Spring 微服务中的 Kafka 与 RabbitMQ
下一篇
开通oss服务