Supervisor托管jar包出现OOM问题分析及解决方案

supervisor托管jar包出现OOM问题分析及解决方案

问题发现

使用superviosr托管jar包,启动命令为java -jar xxx.jar形式,启动11个Java微服务,按启动顺序启动java进程后,eureka中注册的服务只能稳定7-9个,其他的服务在supervisor中的状态显示为running,但是查看日志一直在反复重启,且正常启动的7到9个服务中也会出现反复重启的情况。反复重启时进程在supervisor中状态在running和starting之间转换,重启一定次数后supervisor状态变为back off报错状态。

查看java进程日志,一般有三种错误导致重启:

  • eureka无法连接Completed shut down of DiscoveryClient;
  • task-service无法连接;
  • 内存溢出;

前两种错误一般是因为eureka服务和task-service服务重启导致的,所以根本原因是因为内存溢出。

 

Supervisor开机自启说明:

网上给出supervisor的自启方式都是编写systemd文件,使用systemd启动,测试发现,如果在supervisord.conf配置文件中使用/etc/profile中的环境变量则无法正常启动,例如进程启动命令为

command = %(ENV_JDK_PATH)s %(ENV_SMALL_JVM_MEMORY)s -jar %(ENV_ONESRM_PATH)s/libs/%(program_name)s-%(ENV_PROGRAM_VERSION)s.jar %(ENV_RUN_PARAM)s

其中JDK_PATH等变量为/etc/profile中的环境变量。

若使用systemd开机自启,无法载入环境变量,supervisor启动报错。调研后,决定使用/etc/init.d/after.local文件自启,在文件中首先source /etc/profile,然后使用命令启动supervisor,即可开机自启并载入环境变量。

 

问题分析与解决思路

  • 分析重启原因为内存溢出后,尝试将虚拟机物理内存由8G上调至32G,但是依旧只能启动7到9个进程。
  • 接着又尝试调整了jvm初始化分配内存-Xms与最大使用内存-Xmx,由128m/256m调整为512m/2048m,依然未解决问题。实际上其中占用内存最大的task-service正常启动后,最大内存也只占用1g左右,设置2048m完全够用,基本可以排除是jvm参数的问题。
  • 考虑到虽然config-service,eureka-service,task-service顺序启动时间隔较大,但是剩下的服务几乎是同时启动,是否因为同时启动对系统资源占用较大导致OOM的问题,于是我关闭java自启,手动启动每个服务,等待上一个服务完全启动后再启动下一个,依旧只能启动7-9个服务。
  • 网上查询java进程OOM可能的原因,网上给出一种解决方案,是因为系统限制了最大进程数量,到达最大数量后,可能会导致进程内存溢出,于是按照网上的步骤,将* - nofile 278528添加至/etc/security/limits.conf,确认修改成功后重启,但是依然无法解决问题。
  • 使用top命令查看系统资源占用,发现即使所有java进程启动后,也只占用7g左右的内存,swap都未占用,且不使用supervisor托管java进程,使用同样的jvm内存参数手动启动时,11个进程都可完整启动,所以可以排除是内存不够用导致的。

下图为11个进程全部启动后的资源占用情况

 

  • 由于在服务器环境虚拟机较多,硬件压力大,我在本地电脑安装VMware,搭建了与服务器环境完全相同的suse12sp3环境,并发现可以复现OOM,确定不是服务器环境的问题。
  • 使用suse12自带的系统监控软件,发现supervisor开机启动后,java进程全部启动,并且进程开始遇到OOM重启时,python2进程CPU占用率较大,维持在30%左右,于是我将开机启动的命令由

/usr/bin/python2 /usr/bin/supervisord -c $ONESRM_PATH/conf/supervisord.conf

改为

/usr/bin/supervisord -c $ONESRM_PATH/conf/supervisord.conf

启动supervisor的进程由python2改为python(实际上python也是指向python2的),发现可以解决在java进程自动重启时python2进程CPU占用大的问题,但是依旧不能解决java进程OOM的问题。

  • 测试过程中发现,如果不设置supervisor开机自启,开机后手动使用命令启动supervisor,再启动java,所有java进程都可稳定运行,只有supervisor开机自启时,java进程才会出现OOM的问题,于是对比手动启动与开机自启supervisor两个进程有什么区别,发现只有Control Group不一样,手动启动为user.slice,开机自启为system.slice。
  • 发现两个进程的区别后,在网上查询关于cgroup的相关资料,查询到cgroup可以限制任务数(https://segmentfault.com/a/1190000007468509),于是继续查询相关资料,发现开机自启supervisor的控制组/system.slice/after-local.service的最大任务数被限制为512个,使用systemd-cgtop监控java进程启动时的task情况,发现java进程启动到7-9个时,task数量刚好到512个,一旦超过512个task,整个java进程都会被强行停止,并且重启,反复重启无法启动时,java进程便会报OOM的异常。因为开机自启的进程较多,/system.slice/after-local.service控制组除java进程外的task数量较多,而手动启动supervisor时,/user.slice/user-0.slice除java进程外的task数量少,所以即使java全部启动,也达不到/user.slice/user-0.slice的512task限制,所以手动启动supervisor无OOM的问题。

systemd-cgtop监控界面如下,/system.slice/after-local.service控制组的task数量为511。

 

问题解决方案

发现导致java进程OOM的根本原因后,网上查阅相关资料,task数量为512限制可在文件/sys/fs/cgroup/pids/system.slice/after-local.service/pids.max中查看,将

DefaultTasksMax=2048

添加至/etc/systemd/system.conf文件,可永久修改cgroup最大task限制。

修改为2048后,supervisor开机自启,启动java进程再无OOM问题,至此问题解决。

总结

supervisor托管java进程报内存溢出错误,并且java进程反复重启,导致supervisor报错无法重启java进程的问题,在确定物理内存足够用,并且jvm内存参数调整后依旧无法解决问题的情况下,极有可能是Linux对进程数量限制,suse12sp3中cgourp对一个控制组默认限制最大进程数量为512,在11个java进程都启动时,task数量能达到700左右,所以将cgroup的进程数量增加即可解决问题。

 

对Linux基础知识的缺乏导致这次问题解决时间较长,而java进程报Out Of Memory错误,导致前期分析问题的思路一直在调整内存和jvm参数上,未考虑过Linux进程限制,实际完整的报错为

java.lang.OutOfMemoryError: unable to create new native thread

后面的unable to create new native thread说明可能是系统最大进程限制导致的OOM。

问题解决后,查阅了关于Linux进程限制的相关资料

https://blog.csdn.net/redenval/article/details/91414602

这篇文章详细说明了Linux对进程存在限制的几处,之后再遇到相关问题,可以参考解决。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值