原创:西狩

编写日期 / 修订日期: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,启动成功。

[admin@zw-yunying-172.30.1.135 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”

参考资料

记一次 RocketMQ broker 因内存不足导致的启动失败的更多相关文章

  1. 最小配置启动SQL SERVER,更改SQL Server最大内存大小导致不能启动的解决方法

    如果存在配置问题而无法启动服务器,则可以使用最小配置启动选项来启动 Microsoft SQL Server 实例. 这就是启动选项 -f. 使用最小配置启动 SQL Server 实例会自动将服务器 ...

  2. 记:cloudstack--gluster主存储上的一个文件损坏导致SSVM启动失败

    cloudstack的系统vm(ssvm不停的重建失败).- 1.cloudstack-management 的关键日志 这行 cannot read header 'mnt.......':Inva ...

  3. 记因PHP的内存溢出导致的事故之解决

    如果对您有用记得关注,更多干货. 今天上午刚到公司,就有同事在公司群里反映某个计划任务出现问题了.我就怀着刨根问底的心,去查看了log.发现挺有意思的一个问题,PHP内存溢出导致脚本执行失败.那就一起 ...

  4. rocketMQ broker 分发并处理请求

    使用 netty 监听端口 // org.apache.rocketmq.remoting.netty.NettyRemotingServer#start ServerBootstrap childH ...

  5. 解Bug之路-记一次JVM堆外内存泄露Bug的查找

    解Bug之路-记一次JVM堆外内存泄露Bug的查找 前言 JVM的堆外内存泄露的定位一直是个比较棘手的问题.此次的Bug查找从堆内内存的泄露反推出堆外内存,同时对物理内存的使用做了定量的分析,从而实锤 ...

  6. 《JavaScript 闯关记》之垃圾回收和内存管理

    JavaScript 具有自动垃圾收集机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存.而在 C 和 C++ 之类的语言中,开发人员的一项基本 ...

  7. RocketMQ一个新的消费组初次启动时从何处开始消费呢?

    目录 1.抛出问题 1.1 环境准备 1.2 消息发送者代码 1.3 消费端验证代码 2.探究CONSUME_FROM_MAX_OFFSET实现原理 2.1 CONSUME_FROM_LAST_OFF ...

  8. RocketMq灰皮书(二)------本地部署启动MQ

    RocketMq灰皮书(二)------本地部署启动MQ Windows10本地部署RocketMQ 在上一篇文章中,我们对rocket的几个基本概念进行了介绍,也了解了业内几大消息中间件的区别.在本 ...

  9. 记一次centos6升级salt-minion启动失败的问题

    记一次centos6升级salt-minion启动失败的问题 作者:耀耀 blog:https://www.liuyao.me 一.起因 升级Salt-minion后 使用/etc/init.d/sa ...

随机推荐

  1. SSH,公钥,私钥的理解

    参考大佬的文章:https://blog.csdn.net/li528405176/article/details/82810342    https://www.cnblogs.com/Bravew ...

  2. Angular入门,开发环境搭建,使用Angular CLI创建你的第一个Angular项目

    前言: 最近一直在使用阿里的NG-ZORRO(Angular组件库)开发公司后端的管理系统,写了一段时间的Angular以后发现对于我们.NET后端开发而言真是非常的友善.因此这篇文章主要是对这段时间 ...

  3. 根据json数据和HTML模板,渲染嵌套的HTML

    2020-12-22 11:53:23 星期二 场景, HTML模板是多个div嵌套, 里边有列表, 也有键值对, 与之匹配的有一个json数据, 需要根据json去渲染这个HTML DOM 示例截图 ...

  4. 图解Python中深浅拷贝

    在工作中,常涉及到数据的传递,在数据传递使用过程中,可能会发生数据被修改的问题.为了防止数据被修改,就需要在传递一个副本,即使副本被修改,也不会影响原数据的使用.为了生成这个副本,就产生了拷贝.今天就 ...

  5. winform关闭登录窗体打开主窗体的方法

    实际使用 Program.cs代码 //声明一个线程 private static System.Threading.Mutex mutex; /// <summary> /// 应用程序 ...

  6. ASP.NET中二进制流下载文件时进度条的使用

    说明 在下载大文件时,页面会进入假死状态,于是加上一个进度条以标识后台程序正在运行. 目前,做的进度条并不是实时的,并不会根据程序执行的进度正确显示. 目前是将进度条定时加载到90%,然后停止,等待后 ...

  7. Nuget 安装本地包文件

    Install-Package SomePackage -Source C:\PathToThePackageDir\

  8. vs2010新特性

    下面列出了一些新的功能:1.代码编辑器新的代码编辑器使代码更易于阅读.可以通过按 CTRL 并滚动鼠标轮放大文本.此外,单击 Visual C# 或 Visual Basic 中的符号时该符号的所有实 ...

  9. (三)文件的链接(ln)

    一.链接的分类及特点 当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link ...

  10. .NET 5 开源工作流框架elsa技术研究

    今天假期第一天,研究了.NET 5开源工作流框架elsa,现在分享给大家. 一.框架简介 elsa是一个开源的.NET Standard 工作流框架,官方网站:https://elsa-workflo ...