Linux性能调优实践------基于应用系统故障的调优案例
https://blog.csdn.net/xiaochenXIHUA/article/details/165126522
一、JavaWeb应用系统异常案例
群晖NAS上安装部署国产开源博客、知识库或网站系统Halo的实操保姆级教程
https://blog.csdn.net/xiaochenxihua/article/details/155097629
Nginx的反向代理与正向代理及其location的配置说明
https://blog.csdn.net/xiaochenxihua/article/details/151022106
1.1、故障现象
基于Java使用vue+spring boot框架开发的开源建站工具Halo的web系统,可以正常登录后台编辑文章内容并保存发布,但是访问这个发布的内容后不能正确显示,该界面直接显示"404 Not Found.iissnan_next主题不存在或已删除",其它内容都可以正常访问,如下图所示:

1.2、故障分析
由于这个一个halo的web系统,因此可以登录到这个web系统的服务器上查看运行的所有进程信息,找到该halo进程内容,并查看该halo程序的运行日志。
bash
#故障排查
#1-查看服务器上的所有进程信息
ps -ef
#2-【可选】若看到nginx,可以查看nginx的配置文件nginx.conf里面的server内容是否正确(默认配置是在/etc/nginx)
#也可以通过【find / -name nginx.conf】命令查找
cd /etc/nginx
vi nginx.conf
#2.1-查看nginx的访问日志是否可以查看到具体的问题(浏览器刷新后再次访问有故障的页面)
tail -100f /var/log/nginx/access.log
#3-通过nginx的访问日志无法确定具体的问题,继续查看该halo程序的日志
#3.1-通过端口获取到对应的进程PID(如:3765)
lsof -i :8090
#3.2-通过进程PID获取到该进程信息
ps -ef | grep 3765
#3.3-找到程序的日志并查看(浏览器刷新后再次访问有故障的页面即可查看到对应的报错信息"打开的文件过多")
find / -name halo.log
tail -f /home/halo/.halo2/logs/halo.log

目前找到了这个故障提示"/root/.halo/templates/themes/iissnan_nex:打开的文件过多"但是自己又不知道如何解决,则可以将这个报错信息复制一份到浏览器中查看或直接咨询AI.
1.3、故障解决
bash
#排查到是"打开的文件过多",则可以查看系统的资源限制
#1-查看所有的资源限制配置项及其值(可以看到【open files】的值是1024)
ulimit -a
#2-临时调大【open files】的值后重启halo服务(在浏览器中刷新后再次访问故障的halo页面是否正常)
ulimit -n 102400
ulimit -n
systemctl restart halo.service
#3-确定正常后持久化这个【open files】的值
vi /etc/security/limits.conf
#3.1-【vi /etc/security/limits.conf 】文件末尾添加如下内容后保存退出
* soft nofile 655360
* hard nofile 655360
#3.2-查看指定进程的资源占用情况
#获取到halo服务进程的PID(如:1670)
ps -ef | grep halo
#查看该halo服务器PID的资源占用情况(也可以确定该资源是否使用了我们修改后的配置)
cat /proc/1670/limits
1.4、扩展内容
| systemd与cgroups的资源管理机制 |
|---|
| systemd 是 Linux 的服务管理器,用于启动和管理系统进程(如服务、守护进程)。它管理服务的启动顺序、依赖关系、日志记录等功能。 |
| cgroups 是 Linux 内核提供的一个功能,用于对进程组施加资源限制。它可以控制 CPU、内存、IO、网络带宽等资源。 |
| systemd 从 systemd 205 版本起与 cgroups 集成,systemd 通过 cgroups 来对服务进程进行资源限制。可以控制 CPU、内存、IO 等资源 |
| systemd 和cgroups都可以控制 CPU、内存、IO 等资源。**cgroups 通过直接操作文件系统(如 /sys/fs/cgroup/)进行配置;**而 systemd 提供了更高级别的接口,通过编辑 .service 文件即可轻松设置 CPU、内存、IO 等资源限制,而底层由 cgroups 来执行这些限制。 |
| 在Linux3.10内核以后的版本中,Systemd替代了之前的SysV,前面介绍的【/etc/security/limits.conf】文件仍然可以使用,但是它的作用范围缩小了很多,新内核版本中, limits.conf文件的配置,只适用于通过PAM认证登录用户的资源限制,而对于systemd的service的资源限制将不会生效。 |
| 对于systemd service的资源设置,则需修改全局配置,全局配置文件分别是【/etc/systemd/system.conf】和【/etc/systemd/user.conf】,其中,system.conf是系统全局使用的配置文件,user.conf是用户级别使用的配置文件。 |
bash
#针对单个Service,可以直接修改配置文件,并马上生效
vi /etc/systemd/system/halo.service
#编辑【/etc/systemd/system/halo.service】服务文件中的[Service]段中添加如下内容
LimitNOFILE=655360
LimitNPROC=655360
#保存退出,让配置生效
systemctl daemon-reload
systemctl restart halo.service
systemctl status halo.service
#查看配置是否生效
#获取到halo服务器的PID(如:9250)
ps -ef | grep halo
#查看halo服务PID的资源情况
cat /proc/9252/limits


二、ulimit 控制进程使用系统资源机制
2.1、ulimit的使用场景
ulimit 是 shell 内置命令,底层调用 POSIX setrlimit() / getrlimit() 系统调用,在内核维护每个进程独立的 rlimit 资源限制;作用域是【单个进程】,fork/exec 时继承给子进程,不是 cgroup(cgroup 是进程组限制)
| ulimit的使用场景 |
|---|
| ulimit 是 Linux 中用于控制和管理进程使用系统资源的命令。它可以设置用户进程在系统中能够使用的资源限制,防止单个进程消耗过多的系统资源,从而保证系统稳定性。 |
| ulimit有3种使用方法: 《1》直接在shell命令终端执行ulimit命令 这种方法的资源限制仅仅在执行命令的终端生效,在退出或关闭终端后,设置失效,并且这个设置不影响其它shell终端。 《2》在用户环境变量中加入 如果用户使用的是bash,那么就可以在用户目录的环境变量文件【.bashrc】或【.bash_profile】中加入"ulimit -u 128"来限制用户最多可以使用128个进程。 《3》在应用程序的启动脚本中加入 如果应用程序是tomcat,那么就可以在tomcat的启动脚本startup.sh中加入"ulimit -n 65535"来限制用户最多可以使用65535个文件描述符。 |
| 有时候为了方便起见,也可以将用户资源的限制统一由一个文件来配置,这个文件就是【/etc/security/limits.conf】该文件不但能对指定用户的资源进行限制,还能对指定组的资源进行限制。该文件的使用规则如下: <domain> <type> <item> <value> 其中: * domain 表示用户或组的名字,还可以使用 * 作为通配符,表示任何用户或用户组。 * type 表示限制的类型,可以有两个值,soft 和 hard,分别表示软、硬资源限制。 * item 表示需要限定的资源名称,常用的有nofile、cpu、stack、noproc等。分别表示最大打开句柄数、占用的cpu时间、最大的堆栈大小和最大用户进程数。 * value 表示限制各种资源的具体数值。 |
| 在设置进程的资源限制的时候,需要同时设置软限制和硬限制,超出软规则的限制会进行警告,但是不能超过硬规则的限制。 一般线上服务器应用,推荐在【/etc/security/limits.conf】文件中进行资源的设置,设置完成后,要保证设置生效: 1. 需要退出当前ssh登录的终端,再次登录后,ulimit资源设置就已经生效了; 2. 若要让系统上的应用也能生效的话,必须在新的终端下重启应用系统服务,这样,之前的设置才能生效,这个非常重要。 |
2.2、ulimit的使用经验
| ulimit的使用经验 | 说明 |
|---|---|
| nofile配置规则 | nofile用来设置最大打开句柄数、noproc用来设置最大用户进程数,这两个优化选项是最经常用到的,对待这两个参数的设置,需要特别注意的是:【nofile不能设置过大】如:设置为unlimited就会报错,看如下操作: root@kylinserver \~# ulimit -n unlimited -bash: ulimit: open files: 无法修改 limit 值: 不允许的操作 这个问题是由内核导致的,不同的版本,可以配置的最大值都不一样,要提高这个值,可以通过修改【/proc/sys/fs/nr_open】的值来动态提高最大打开文件句柄数。 |
| nofile配置不生效场景 | limits.conf 文件用于设置通过 PAM(可插拔认证模块)登录的用户的资源限制(即:当用户通过使用 PAM 的服务(比如 SSH、终端等)登录系统时,limits.conf 中定义的限制将会生效)。 注意:这些设置不会影响由系统管理的服务(如: systemd 管理的服务)。系统管理服务的资源限制是由其配置文件或服务单位文件(.service 文件)设定的,limits.conf 对 systemd 服务默认无效。 |
bash
#nofile的配置示例
#1-查看可设置的最大文件句柄数
cat /proc/sys/fs/nr_open
#2-临时调整文件句柄【open files】值
ulimit -n 1073741816
ulimit -n
#3-临时调整文件句柄【open files】的值设置的很大超过最大句柄限制(会提示"-bash: ulimit: open files: 无法修改限制:Operation not permitted")
ulimit -n 1073741866
#3.1-若还是需要调大这个文件句柄,则可以修改【/proc/sys/fs/nr_open】文件中的值,但是也不能无限大的设置
echo 1073741866 > /proc/sys/fs/nr_open
ulimit -n 1073741866
ulimmit -n
三、ulimit与systemd对比
3.1、ulimit与systemd对比
| 特性 | ulimit / rlimit | systemd cgroup(MemoryMax/CPUQuota) |
|---|---|---|
| 管控对象 | 单个进程,继承 | 进程组 (cgroup),分层,一组进程总和 |
| CPU 限制 | RLIMIT_CPU:累计 CPU 时间(总秒数),耗尽杀进程;不能限制 CPU 使用率百分比 | CPUQuota:限制 CPU 使用率(%),节流,不会直接杀进程 |
| 内存限制 | RLIMIT_AS:虚拟地址空间;无法限制物理内存 RSS;RLIMIT_RSS Linux 无效 | MemoryHigh/MemoryMax:物理内存,cgroup OOM kill |
| 作用域 | 进程 fork 继承;不影响已有进程 | 所有加入 cgroup 的进程;动态生效 |
| 层级 | 无层级,每个进程独立 | cgroup 树,父 slice 限制会被继承 |
| 标准 | POSIX 标准接口 | Linux cgroup 内核扩展 |
结论:
- ulimit 不能限制物理内存占用,不能限制 CPU 使用率百分比,只能限制进程总 CPU 时间、虚拟内存、文件句柄、进程数等;
- 如果你要限制服务物理内存上限,必须使用 systemd MemoryMax(cgroup v2),而不是 ulimit;
- systemd 服务可以同时配置:
LimitNOFILE(rlimit,ulimit) +MemoryMax/CPUQuota(cgroup)两套限制,两套机制并行在内核独立生效。
3.2、systemd限制指定服务的资源使用
systemd服务脚本详解与管理命令
https://blog.csdn.net/xiaochenxihua/article/details/149216896
| systemd限制指定服务的资源使用 | 说明 |
|---|---|
| LimitNOFILE | 限制最大打开文件描述符数量 |
| LimitNPROC | 限制的进程数量 |
| CPUQuota | 只限制单核心 ,CPUQuota=100% = 最多 1 核;200% = 最多 2 核。 通用计算公式:CPUQuota值 = 核心总数 × 整机期望占比 |
| MemoryMax | 硬限制,进程总内存(RSS + 缓存等)达到就触发 OOM kill。单位:K/M/G/T |
注意:
- 值【infinity】表示不限制资源的使用。
- infinity表示的不限制资源使用,只是cgroup 层面无上限,整机内存耗尽,内核全局 OOM 依旧会杀进程。
bash
#服务资源的配置限制
[Service]
# ========== CPU 限制 ==========
# CPU权重(相对值,默认100;越小优先级越低)
CPUWeight=100
# CPU配额:单位微秒。例:CPUQuota=20% → 20000
# 含义:每100ms最多使用20ms CPU时间,单核心上限
CPUQuota=20%
# CPU亲和性,绑定到指定CPU核心(可选)
# CPUAffinity=0,1
# ========== 内存限制 ==========
# 内存硬上限,到达直接OOM杀死进程
MemoryMax=512M
# 内存软上限,内存紧张时主动回收,不会直接杀进程
MemoryHigh=400M
# 最低预留内存(一般不用)
# MemoryMin=128M
# 交换分区使用上限
MemorySwapMax=256M
| 项目 | LimitCPU | CPUQuota |
|---|---|---|
| 底层机制 | POSIX rlimit / setrlimit | Linux cgroup v2 cpu 控制器 |
| 管控对象 | 单个进程,fork 继承 | cgroup 进程组(一组所有进程总和) |
| 限制维度 | 累计 CPU 总执行秒数 | CPU 使用率带宽(每周期可使用 CPU 时间) |
| 超限行为 | 先 SIGXCPU,硬限制到直接 SIGKILL 杀死进程 | 线程节流降速,不杀进程 |
| 单位 | 秒,支持 soft:hard | %,100% = 1 核 |
| 常驻服务 | ❌ 不推荐,跑久了会被杀 | ✅ 推荐,限制 CPU 使用率 |
| 瞬时峰值 | 无带宽节流,瞬间可以打满多核 | 瞬时可以短暂冲高,平均被封顶 |
注意:常驻服务一般不要配置 LimitCPU,服务长时间运行累计 CPU 时间会不断上涨,最终被 SIGKILL 干掉!!!
bash
[Service]
# 示例:软限制300秒,硬限制360秒
LimitCPU=300:360
bash
[Service]
# 4核整机20%算力,0.8核
CPUQuota=80%
bash
#验证命令
# 查看LimitCPU
systemctl show yourservice -p LimitCPU
# 查看CPUQuota
systemctl show yourservice -p CPUQuota
# 查看进程rlimit(RLIMIT_CPU)
cat /proc/<PID>/limits
# 查看cgroup cpu带宽
cat /sys/fs/cgroup/system.slice/yourservice.service/cpu.max