记录一次线上服务器内存持续增高问题梳理及总结

一、问题现象

11.png

二、项目背景

  1. 线上web服务是nginx+php7+mysql
  2. 线上服务器配置:8核16G
  3. php-fpm配置:

    pm = static
    pm.max_children = 500
    ;pm.max_requests = 5000 //此配置没有打开

三、内存持续增高原因分析

  1. 查看了php-fpm相关配置后,发现系统是静态机制,并且没有配置max_requests项,没有退出机制
  2. php进程长期存在导致内存积压无法释放,所以内存消耗持续升高
  3. nginx+php-fpm运行原理?【https://juejin.im/post/58db7d...

    cgi、fast-cgi、php-fpm之间的关系?
    
    我的理解:
        1.webserver最开始只能处理静态请求,后来出现了动态请求,比如php程序,然而webserver是无法处理php程序的,这个是时候怎么办?
        2.交给php解释器来处理动态php请求
        3.那么webserver可以将动态请求交给php解释器处理,那么webserver如何与php解释器进行通信呢?【webserver就是nginx/apache】
        4.这个时候就出现了cgi,cgi协议就是webserver和php解释器进行通信的协议
        5.webserver在每次请求过来都会fork一个cgi进程进行处理,处理完成后返回,如果有10k请求,那么就fork 10k个进程,显然对系统资源很浪费
        6.这时对cgi做了优化,出现了fast-cgi,fast-cgi在请求处理完成后不会直接kill掉这个进程,而是继续保留请求下一次请求,这样一个进程就能处理多个请求,不用每次都fork进程减少了系统资源浪费
        7.而php-fpm就是fast-cgi的实现,并且提供了进程管理的功能,包含master和worker两种类型的进程
        8.master进程只负责监听端口,接收来自webserver的请求,worker进程有多个,每个worker进程内部都嵌入了一个php解释器,是php代码真正执行的地方
    
  4. 一些php-fpm参数理解:

    pm = dynamic # 三种类型选择,static/dynamic/ondemand
    pm.max_children = 5 # php-fpm的worker进程最大数量
    pm.start_servers = 3 # php-fpm启动时候,启动的worker数量
    pm.min_spare_servers = 2 # php-fpm最小空闲进程数量,每时每刻最少也有2个是空闲的进程
    pm.max_spare_servers = 4 # php-fpm最大的空闲进程数量
    pm.max_requests = 200 # 单个进程处理请求数量达到200就会kill掉该进程重启,进程一直存活容易发生内存泄漏
    

四、解决方案

  1. 通过现在php-fpm的配置可以得知,php-fpm没有设置max_requests,代表进程一直不会退出,每个请求完成后php-cgi会回收内存,但是不会释放给操作系统,所以大量内存被php-cgi占用。
  2. 官方的解决办法就是降低PHP_FCGI_MAX_REQUESTS的值,对应php-fpm配置中的max_requests
  3. 所以,只要重新设置 max_requests 的值,让进程达到这个值后自动重启释放内存即可

    # php-fpm.conf配置
    pm = static
    pm.max_children = 500
    pm.start.servers = 100
    pm.min_spare_servers = 20
    pm.max_spare_servers = 100
    pm.max_requests = 2000
  4. 修改完配置后对php-fpm进行重启

    //查询当前fpm的master进程号
    ps aux|grep php-fpm | grep master
    //平滑重启fpm,42891是master进程号
    kill -USR2 42891
    //立即终止fpm
    kill -QUIT 42891
    //查看状态
    ps aux|grep php-fpm
      
      
    //备注:
    php 5.3.3 以后的php-fpm 不再支持 php-fpm 以前具有的 /usr/local/php/sbin/php-fpm (start|stop|reload)等命令,所以不要再看这种老掉牙的命令了,需要使用信号控制:
     
    master进程可以理解以下信号
     
    INT, TERM 立刻终止
    QUIT 平滑终止
    USR1 重新打开日志文件
    USR2 平滑重载所有worker进程并重新载入配置和二进制模块
  5. 参考:https://www.cnblogs.com/cocol...
阅读 1.2k

推荐阅读