原创 吴就业 180 0 2024-02-04
本文为博主原创文章,未经博主允许不得转载。
本文链接:https://wujiuye.com/article/701f6d16de034d09b1a392d68ecfe87a
作者:吴就业
链接:https://wujiuye.com/article/701f6d16de034d09b1a392d68ecfe87a
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。
Go写的文件上传系统又内存使用告警了!
这种问题当然是先上pprof。然而,经过排查,发现即没有goroutine泄漏(goroutine总数只有一百多),也没有堆内存使用问题(内存占用才两个多mb)。
当我使用Top命令查看,进程确确实实占了非常多的内存。通过查看Pod监控,内存呈上升曲线,这条曲线就足以说明存在内存泄漏了。
上传系统部署四个大区,唯独国内出现内存占用90%+告警和出现容器故障重启,唯一的区别是国内的文件上传使用的是Ceph,我们在容器镜像中打入ceph的c链接库,并用go-ceph库去访问ceph文件系统。经过初步使用pprof排除了go程序本身代码逻辑导致的内存泄漏,加之只有国内出现,我们怀疑是ceph库的问题。
为了进一步找出问题,我们找了一些资料,怎么排查底层c库内存泄漏问题,于是找到了jmalloc这个库。
要使用jmalloc提供的工具分析内存泄漏,首先是要将使用malloc标准库替换为使用jmalloc标准库。好在这个系统是容器部署的,很方便在Dockerfile中安装jmalloc。
FROM k8sharbor.xxx.xx/xxx/golang:1.16.15-c7.9-ceph
........
# 安装jemalloc
RUN mkdir -p /usr/local/share/jemalloc
RUN cd /usr/local/share/jemalloc
RUN wget https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2
RUN tar -xf jemalloc-5.3.0.tar.bz2
RUN ./jemalloc-5.3.0/configure --enable-prof
RUN make && make install
.......
# 在启动程序之前配置环境变量
ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so
## lg_prof_interval:28,其含义是内存每增加 256MB(2^28,可以根据需要修改),就输出一份内存 profile。
## lg_prof_sample:1,表示每1个事件中有一个会被采样记录。
ENV MALLOC_CONF="prof:true,prof_prefix:/data/logs/jemalloc/jeprof.out,lg_prof_interval:28,lg_prof_sample:1"
上线两天后,发现告警一直没出现,看了下Pod的指标,非常的正常。
很神奇,曲线莫名消失了。
但问题还是会出现,运行几天后,进程还是会因为内存超出Pod的limits被kill掉。
通过jeprof命令工具排查未发现问题。
命令:jeprof --show_bytes --text xxx jemalloc_heprof.out.xx.xx.xxx.heap
可能是因为瞬间涨上去之后,进程挂了,jemalloc也没dump到最后有问题的heap。
没办法,只能通过本地编写测试用例,覆盖所有场景,增加并发模拟线上,逐个场景找问题。
最后发现是jpg文件转webp格式出现的内存泄露,用的库是github.com/chai2010/webp
,后来改成github.com/kolesa-team/go-webp
,问题解决。
参考文献:
声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。
代码中,与tls有关的地方就是发送https请求从s3下载文件,所以检查下载文件调用链路上是否存在可疑的内存泄漏,发现如下疑点。统计了访问日记,发现确实经常出现响应403。所以问题就清晰了,由于403是有body的,没有close响应的body导致的内存泄漏。
记录一次工作中实战的Java内存泄漏问题排查,Pod重启后无法查看现场,但通过gc日记可以确认存在内存泄露,并通过运行一段时间发现有个Java类的实例数量非常高。
因为go标准库实现tls握手性能比较差,在一台8核的机器,只能到达2000这个量级,所以当到达某个临界点的时候,握手占用CPU过高,反过头来影响正常的业务请求,导致了业务请求处理变得十分慢。
一个微服务可能引入非常多的SDK,例如消息中间件kafka的组件、RPC框架dubbo、定时任务调度平台xxl-job的组件,以及提供web服务的jetty/tomcat等。这些组件的初始化是不确定的,那么假如启动初始化过程中,其中某个组件初始化失败了,会发生什么?
基于go提供的基准测试能力编写并发测试用例,为排除脚本本身的性能影响,脚本只实现简单的逻辑,并实现预编译。通过调整虚拟机池策略、cpu数、并行度等,输出调用lua脚本的平均耗时、占用的内存。
订阅
订阅新文章发布通知吧,不错过精彩内容!
输入邮箱,提交后我们会给您发送一封邮件,您需点击邮件中的链接完成订阅设置。