原创:西狩

编写日期 / 修订日期: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. AdaBoost 算法-分析波士顿房价数据集

    公号:码农充电站pro 主页:https://codeshellme.github.io 在机器学习算法中,有一种算法叫做集成算法,AdaBoost 算法是集成算法的一种.我们先来看下什么是集成算法. ...

  2. 2020软件测试工程师面试题汇总(内含答案)-看完BATJ面试官对你竖起大拇指!

    测试技术面试题 1.什么是兼容性测试?兼容性测试侧重哪些方面? 参考答案: 兼容测试主要是检查软件在不同的硬件平台.软件平台上是否可以正常的运行,即是通常说的软件的可移植性. 兼容的类型,如果细分的话 ...

  3. python极简代码之检测元素组成是否相同

    可以检测两个字符串的组成是不是一样的,可以检测两个列表的元素是否一样,可以检测集合里的元素是否一致,可以检测字典里的值是否一致: 1 # !usr/bin/env python3 2 # *-* co ...

  4. EF中使用UnitOfWork

    前言 关于EF5中使用UnitWork,参见另一博文:  https://www.cnblogs.com/masonblog/p/9801162.html 每次提交数据库都会打开一个连接,造成结果是: ...

  5. 【命令】man命令帮助文档详解

    前言:Linux命令分为内建命令和外部命令:内建命令是shell本身自带的,外部命令是是一个可执行程序 我们在使用命令帮助的时候需要钱哦区分命令是内建命令还是外部命令 一.查看一个命令是内建命令还是外 ...

  6. Java源码研究001:关于List的并发修改异常

    这个就是实现一个简单的 ArrayList 的遍历,如果存在一个为"aaa"的值,就添加一个"ccc" package Array; import java.u ...

  7. Atlas 2.1.0 实践(2)—— 安装Atlas

    在完成Atlas编译以后,就可以进行Atlas的安装了.Atlas的安装主要是安装Atlas的Server端,也就Atlas的管理页面,并确保Atlas与Kafka Hbase Solr等组件的集成. ...

  8. js表单简单验证(手机号邮箱)

    1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncod ...

  9. Media Queries媒体查询

    Media Queries能在不同的条件下使用不同的样式,使页面在不同在终端设备下达到不同的页面效果.Media Queries有其自己的使用规则.一个媒体查询由一个可选的媒体类型和媒体特性表达式,使 ...

  10. java 多态 向上造型

    最近在读java 编程思想,在读多态一章时,遇到了一个问题,在此记录一下. 1 package main.demo; 2 3 class Super{ 4 public int filed =0; 5 ...