起因: 每次调用sharp库进行图片合成之后,内存都会涨很多,并且并没有被释放掉,所以开始怀疑是内存泄露导致的。
但是同样的代码,部署在window系统上却并没有出现内存占用过高的问题,然后就陷入了迷茫中。
随后去github上寻求帮助,确定问题是不是和操作系统相关。
于是去找了sharp的issue列表,发现了同样的问题
这里的对话比较长,看了很久,最终总结出来就是:sharp作者表示:该问题并不是内存泄露导致的问题,而是操作系统的内存分配机制不同,而出现的内存占用超出预期。
到这里可以确定并不是内存泄露导致的了(这也解释了为什么在window系统上没有出现此现象)
后面去找了sharp的官方网站,发现了对于内存分配的描述:
网址:sharp.pixelplumbing.com/install#lin…
翻译成中文大概意思是:
大多数基于 glibc 的 Linux 系统(例如 Debian、Red Hat)的默认内存分配器不适合涉及大量小内存分配的长时间运行的多线程进程。 因此,默认情况下,当在运行时检测到 glibc 分配器时,sharp 将限制基于线程的并发的使用。 为了帮助避免碎片并提高这些系统的性能,建议使用备用内存分配器,例如 jemalloc。 那些使用基于musl的Linux(例如Alpine)和非Linux系统的人不受影响。
根据以上提供的信息,解决该问题就需要改变操作系统的默认内存分配器
继续查找了一遍issue,发现反馈该问题的开发者不在少数,并且可以看到,通过设置环境变量LD_PRELOAD来修改内存分配器是可以解决内存占用过高的问题的(看到这个消息就很开心了,至少是可以解决问题了)
但是这个issue还提到的问题是: 如果使用pm2的fork模式没有问题,但如果使用pm2的cluster模式启动项目的话,内存分配器并没有被替换掉,仍然存在内存异常升高的问题。
然后又从sharp的issue列表又转到了pm2的issue列表来寻找解决办法....
最终在这个issue上得到了解决方案
- 首先需要安装
因为我的系统是Ubuntu(我用的是arm CPU架构, 如果是x86架构,安装目录和本文有区别),为了方便所以用的apt-get的方式安装的。 先更新,然后再安装
sudo apt-get update
sudo apt-get install libjemalloc-dev
安装完成后,你可以在系统的lib文件夹下看到该依赖库,路径是/usr/lib/aarch64-linux-gnu/libjemalloc.so
- 添加环境变量LD_PRELOAD
sudo nano /etc/environment
LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so
3. 最后在etc/profile末尾添加
export LD_PRELOAD="/usr/lib/aarch64-linux-gnu/libjemalloc.so"
*注意:2,3步骤是为了在系统重启后仍能加载环境变量LD_PRELOAD,否则需要先source env/environment才会生效
- 如果是使用pm2启动项目的话,需要在pm2.json配置文件上加上env属性,并设置LD_PRELOAD的值
{
"apps": [
{
"name": "your-project",
"cwd": "./",
"script": "./index.js",
"args": "start",
"instances": 0,
"env": {
"LD_PRELOAD": "/usr/lib/aarch64-linux-gnu/libjemalloc.so"
},
"exec_mode": "cluster",
"out_file": "./log-pm2/out.log",
"error_file": "./log-pm2/err.log",
"merge_logs": true,
"autorestart": true
}
]
}
如果不是使用pm2启动项目的话,则需要在启动node进程之前设置一次LD_PRELOAD
LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so node .index.js
完成以上步骤后,重启系统,并重新运行项目,内存占用过高的问题得以解决。
补充:
后续发现因为设置了全局环境变量LD_PRELOAD, 在pm2.son的env设置的LD_PRELOAD其实意义不大了,可以去掉
"env": {
"LD_PRELOAD": "/usr/lib/aarch64-linux-gnu/libjemalloc.so"
},
2023-12-22补充:
后续在我们的项目上采用了k8s集群的方式进行了部署,使用dockerfile的方式来设置镜像。又出现了内存不释放的问题,后面发现还是环境变量设置不生效的问题。
最终发现问题是我使用apt-get的方式获取的libjemalloc不生效, 然后就使用make语句在本地编译了libjemalloc解决了问题。
# dockerfile相关代码示例
# 安装jemalloc
RUN apt-get update && apt-get install -y \
build-essential \
wget \
&& rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2
RUN tar jxvf jemalloc-5.2.1.tar.bz2
RUN cd jemalloc-5.2.1 && ./configure && make && make install
# 设置环境变量LD_PRELOAD为jemalloc库
ENV LD_PRELOAD /usr/local/lib/libjemalloc.so
完整dockerfile
# 使用nodejs最新版本作为基础镜像
FROM node:16
# 更改镜像源为国内镜像源
RUN sed -i s/security.debian.org/mirrors.ustc.edu.cn/g /etc/apt/sources.list
RUN apt-get clean
# 安装jemallocpm2
RUN apt-get update && apt-get install -y \
build-essential \
wget \
&& rm -rf /var/lib/apt/lists/*
RUN wget https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2
RUN tar jxvf jemalloc-5.2.1.tar.bz2
RUN cd jemalloc-5.2.1 && ./configure && make && make install
# 设置环境变量LD_PRELOAD为jemalloc库
ENV LD_PRELOAD /usr/local/lib/libjemalloc.so
RUN apt-get update && apt-get install chromium chromium-driver chromium-l10n -y
# 修改源仓库地址
RUN npm config set registry "https://registry.npmmirror.com"
RUN npm config set sharp_binary_host "https://npmmirror.com/mirrors/sharp"
RUN npm config set sharp_libvips_binary_host "https://npmmirror.com/mirrors/sharp-libvips"
# 创建TLS文件夹
RUN mkdir -p /app/tls/
# 创建户型图编辑器目录
RUN mkdir /public
ADD public /public
# 拷贝TLS证书
ADD cert.pem /app/tls/cert.pem
ADD key.pem /app/tls/key.pem
ADD SimHei.ttf /usr/share/fonts/SimHei.ttf
# 安装pm2
RUN npm install pm2 -g --unsafe-perm
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json到工作目录
COPY package*.json ./
# 安装依赖
RUN npm install --unsafe-perm
# 复制应用程序代码到工作目录
COPY . .
# 暴露应用程序端口
EXPOSE 80
EXPOSE 443
# 启动项目
CMD ["pm2-runtime", "start", "pm2.k8s.config.json"]
还踩了一个Alpine的坑:
Alpine采用的musl内核解析DNS存在超时的问题(参考文章): DNS最佳实践_容器服务Kubernetes版(ACK)-阿里云帮助中心 (aliyun.com)
Solving DNS lookup failures in Kubernetes | Findmypast Tech
所以建议还是尽量使用Debian或者ubuntu搭配libjemalloc
参考链接: