侧边栏壁纸
  • 累计撰写 17 篇文章
  • 累计创建 10 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

springboot微服务应用生产重启/卡死问题排查与解决

骑摩托的程序员
2024-09-03 / 0 评论 / 1 点赞 / 221 阅读 / 1,939 字

springboot微服务应用生产重启/卡死问题排查与解决

现象

公司业务与企业微信相关,客户数量百万以上,每个应用各2台容器,处理业务与企业微信回调,普通业务量并不大,主要流量为企业微信回调与其他三方调用,三方调用接口相应基本1s内,除了业务问题,接口没出现过其他异常情况。数据来源基本上是通过企业微信来,员工几万与客户几百万数据量还可以,正常处理没有什么问题,有些批量操作就要出现卡顿等情况了,比如定时任务,某些定时任务执行时间需要几小时,甚至一天还多,期间服务出现凌晨几乎每天重启,服务卡死无法处理请求的情况。接下来一起来分析解决一下这两个问题。

常用命令

服务部署在Kubernetes,日志虽然有收集在elk中,但是查看不方便,并且不实时,所以我一般直接在Kubernetes Dashboard进入容器查看日志,找到你的容器,点击如图按钮(可能版本不同入口不一样,大同小异)
image
开始之前先介绍几个常用命令

tail

  1. 查看日志文件的最后几行,显示 server.log 文件的最后 100 行
tail filename.log
  1. 查看文件最后20行:
tail -n 20 filename.log
  1. 实时跟踪查看文件末尾的新内容:
tail -f filename.log
  1. 实时跟踪查看多个文件末尾的新内容
tail -f filename1.log filename2.log

这些命令可以结合管道操作符(|)和其他命令一起使用,以提供过滤、搜索等功能。

less

less 是一个用于查看文件内容的工具,它支持分页显示,并且允许用户前后浏览文件。如果你需要使用 less 来查看日志文件,可以直接将 less 命令与日志文件相关联。

例如,如果你有一个名为 app.log 的日志文件,你可以使用以下命令来查看它:

less app.log

在 less 中,你可以使用以下快捷键来浏览文件:
j 箭头键向下移动一行
k 或箭头键向上移动一行
PgDn 下翻页
PgUp 上翻页
/ 始搜索文本 (搜索之后n下一个,N上一个)
q 出 less

grep、sed、awk

Linux文本三剑客超详细教程—grep、sed、awk

重启

每个服务2个pod,每日固定一个pod重启,且均为凌晨2点左右。

分析

应用使用定时任务xxjob,xxjob有部分任务执行效率低下,且有许多异步操作,不太好寻找原因,pod在重启前内存飙升,几乎跑满4G还多。
线上原因无法查看HeapDump(典型的带有堆转储的启动参数可以是:java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./java_pid.hprof -Xmx512m …),只能结合别的xxjob与Prometheus观察服务当时状况,刚重启完内存还不飙升,每天1点开始内存一直上涨,并且没有下降的趋势,排查1点以后的定时任务。发现可疑任务,定时拉去企业微信客户数据,单销售最大客户量2W,分批5个销售5个销售分页拉取,最后合并处理,销售身上还挂着其他属性,数据量蛮大,并且要放到内存中等2W*5个数据一起处理,此处为内存飙升原因,持续不下可能和这里有关系,本地模拟数据量测试,部分原因无法全量模拟,屏蔽掉企业微信调用,直接返回2W客户,观察本地环境情况。

涉及命令

# 查看java应用pid。
jps 
# 查看应用启动参数等其他信息。
jinfo <pid>
# 查看应用程序中用来生成Java虚拟机当前时刻线程的堆栈跟踪的命令行工具。
jstack <pid>
# jmap(Java Virtual Machine Memory Map)是JDK提供的一个可以生成Java虚拟机的堆转储快照dump文件的命令行工具。
# 除此以外,jmap命令还可以查看finalize执行队列、Java堆和方法区的详细信息,比如空间使用率、当前使用的什么垃圾回收器、分代情况等等。
jmap -heap <pid>

本地测试无法复现重启的情况,观察dump日志发现部分业务数据长期占用内存,没有及时清楚部分无用list,且数据量大,怀疑此处为导致重启的罪魁祸首。
首先本地模拟数据测试原方法堆栈情况,启动项目,调用可以代码,本地生成dump文件

jmap -dump:format=b,file=d:/dump.hprof <pid>

使用dump分析工具MAT

预留空位,待招租。

代码如下

# 版本1
# 分页获取5个销售的所有客户
List<Customer> customers = batchGetCustomersByUser(users);

if (!customers.isEmpty()) {
	//保存客户基本信息,并返回 客户ID列表
	// customerIdRelationMap: 客户ID列表
	final Map<String, Long> customerIdRelationMap = this.saveCustomerBasicInfo(customers);

	Map<String, Long> userIdRelationMap = getUserIdRelationMap(users);
	Map<String, Long> ignoreMembers = new ConcurrentHashMap<>();

	//转换客户跟进备注信息
	final Map<Long, Set<CustomerFollower>> followers = new HashMap<>();
	final Map<Long, Set<CustomerFollowMobile>> followMobiles = new HashMap<>();
	final Map<Long, List<WecomWrappers.MarkCustomerTag>> markCustomerTags = new HashMap<>();
	Utils.analysisToDataSchema(customerIdRelationMap, userIdRelationMap, customers, followers, followMobiles, markCustomerTags, ignoreMembers);
	if (!ignoreMembers.isEmpty()) log.warn("ignoreMembersData-{}", ignoreMembers);


	//保存客户成员跟进信息
	this.saveCustomerFollowers(followers);
	//保存客户成员备注手机号信息
	this.saveFollowMobiles(followMobiles);
	//保存客户成员备注标签信息
	this.saveFollowTags(authArgs, markCustomerTags);
}

猜测版本一中问题:
分页获取customers,customers中极端数据量可能为单销售2W客户,也就是10W客户,如果整个方法中10w数据一直存活,再扩展手机号,基础信息等其他数据,可能指数级数据均不能垃圾回收,可能导致OOM,修改为如下版本,大量数据使用结束之后及时清除,及时允许垃圾回收器回收,不用等到整个方法结束。


# 版本2

# 分页获取5个销售的所有客户
List<Customer> customers = batchGetCustomersByUser(users);

if (!customers.isEmpty()) {
	//保存客户基本信息,并返回 客户ID列表
	Set<CustomerInfo> customerInfos = getCustomerInfos(customers);
	final Map<String, Long> customerIdRelationMap = this.saveCustomerBasicInfo(customerInfos);

	//userIdRelationMap: 获取用户的具体信息的列表,通过成员ID列表 到system服务中获取数据
	Map<String, Long> userIdRelationMap = getUserIdRelationMap(users);
	Map<String, Long> ignoreMembers = new ConcurrentHashMap<>();

	//转换客户跟进备注信息
	Map<Long, Set<CustomerFollower>> followers = new HashMap<>();
	Map<Long, Set<CustomerFollowMobile>> followMobiles = new HashMap<>();
	Map<Long, List<WecomWrappers.MarkCustomerTag>> markCustomerTags = new HashMap<>();
	Utils.analysisToDataSchema(customerIdRelationMap, userIdRelationMap, customers, followers, followMobiles, markCustomerTags, ignoreMembers);
	if (!ignoreMembers.isEmpty()) {
		log.warn("ignoreMembersData-{}", ignoreMembers);
	}

	customers.clear();
	userIdRelationMap.clear();
	ignoreMembers.clear();

	//保存客户成员跟进信息
	this.saveCustomerFollowers(followers);
	followers.clear();
	//保存客户成员备注手机号信息
	this.saveFollowMobiles(followMobiles);
	followMobiles.clear();
	//保存客户成员备注标签信息
	this.saveFollowTags(authArgs, markCustomerTags);
	markCustomerTags.clear();
}

优化之后dump分析工具查看

《广告招租》

卡死

欲知后事如何,请看下回分解。

1

评论区