关于php-fpm最大连接数和nginx限流

前言少叙,直奔主题,聊一聊php-fpm最大连接数和nginx限流。

php-fpm 如何设置最大连接数

php-fpm 设置最大连接数有三种方式:

  • pm = dynamic:子进程的数量是动态变化的,由以下几个参数决定:

    • pm.max_children = 5 # 最大子进程数
    • pm.start_servers = 2 # 启动时的进程数,默认情况下,等于 min_spare_servers + (max_spare_servers - min_spare_servers) / 2
    • pm.min_spare_servers = 1 # 最小空闲进程数,如果空闲进程小于此值,则创建新的子进程
    • pm.max_spare_servers = 3 # 最大空闲进程数,如果空闲进程大于该值,则进行清理
  • pm = ondemand:进程在有需求时才产生(当请求时才启动,与 dynamic 相反,dynamic 是在服务启动时 pm.start_servers 个子进程就启动了)最大子进程数受 pm.max_children 限制,该模式下闲置进程在持续闲置了 pm.process_idle_timeout 秒后会被杀掉。

  • pm = static:子进程的数量是由 pm.max_children 指令来确定的,启动时即生成 pm.max_children 个子进程。

选择哪个模式

ondemand 模式,闲置时可以确保最小占用内存,但是每次创建子进程时都会消耗cpu,性能最差,只适合主机内存很小的场景,不推荐在生产环境使用该模式。

static 模式,性能最强,但是会占用较多系统资源。

dynamic 模式,比较均衡,闲置时不会占用过多资源,有流量时也可以应付。

建议当机器内存很大,或者机器只用于php单一用途时,使用 static 模式来确保最强性能;如果内存不是很大,或者机器上还跑了其它应用,建议使用 dynamic 模式;ondemand 模式只适应于个人的小型站点。

根据机器情况确定好模式后,就需要确定具体的数值了,应用不同,对cpu和内存的消耗也不同,一般来说有以下参考:

  • cpu运算密集型的应用,pm.max_children 不要超过cpu核数
  • 根据每个子进程占用的内存数来做大概计算,pm.max_children 可以等于 可用内存 / 子进程内存
  • 设置 pm.max_requests,避免因为内存泄漏导致子进程占用过多内存

实际中,最好是对服务器做一下压力测试,以确定具体应用的真实消耗来调整参数。

nginx限流

理想情况下,服务器有着很强的性能,不管来多少请求都能扛得住,可实际使用中,突发的大流量很可能会超出设置好的最大连接数,一旦达到这种情况,当有新的请求到来时会没有子进程可用,nginx就会处在等待状态,并且请求会积压,导致服务器负载越来越大,新的请求处理不了,旧的请求耗时过久,表现出来就是页面长时间无反应,过了一会儿收到502或504错误,很影响用户体验。
而遇到这种情况,要么是手动重启php-fpm,要么是等待流量过去,php慢慢的消化掉所有请求,但这时旧的请求其实已经没什么用了,因为用户不会一直等待,他可能已经关掉了页面。
因此,生产环境中可以配合nginx限流设置,来确保php服务器不会宕机。

配置如下:

# http 标签里增加代码:
limit_req_zone $server_name zone=apiRateLimit:10m rate=10r/s;

# server 标签里增加代码:
limit_req zone=apiRateLimit  burst=20 nodelay;

# 示例:
http {
    limit_req_zone $server_name zone=apiRateLimit:10m rate=10r/s;
    
    server {
        location / {
            limit_req zone=apiRateLimit  burst=20 nodelay;
        }
    }
}

代码解释:

  • limit_req_zone
    代码格式:limit_req_zone key zone rate
    key :定义限流的对象,有几种选择:

    • binary_remote_addr
      基于 remote_addr(客户端IP) 来做限流

    • uri
      不带参数的请求地址

    • request_uri
      带参数的请求地址

    • server_name
      服务器名称,Nginx配置中,server标签里的server_name值

    zone:定义共享内存区来存储访问信息, apiRateLimit:10m 表示一个大小为10M,名字为 apiRateLimit 的内存区域。1M能存储 16000 个IP地址,10M可以存储16W IP地址。

    rate:最大访问速率,rate=10r/s 代表每秒最多处理10个请求。nginx 是以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100ms处理一个请求,这就意味着,自上一个请求处理完后,若这100毫秒内又有请求到达,将会被拒绝处理。

  • limit_req
    zone=apiRateLimit,表示使用前面定义好的 apiRateLimit
    burst=20,最大突发请求数,超过 rate,低于 burst 的请求,会被放入队列等待处理,超过 burst 的会被拒绝。
    nodelay,针对的是 burst,意思是队列里的请求不加延迟,立即处理。

    burstnodelay 这两个概念有些绕,我的理解是:

    • 假设 rate=10r/s,未设置 burst,当每秒请求数大于10时,多出来的请求会被抛弃,直接返回503,在压测工具中的表现就是平均响应时间很快,但是错误率很高。需要注意的是,实际应用中这种设置很不实用,如前所述,10r/s 代表每100ms处理一个请求,而这100ms之内的其它请求会被拒绝,这也就意味着,哪怕每秒请求数不多,但是很密集,比如很常见的一个页面同时调两个接口,只要第一个接口的响应时间在100ms以内,第二个接口的访问就会被拒绝。

    • 假设 rate=10r/s,burst=20,没加nodelay,当每秒请求数大于10,小于20时,所有的请求都会被处理,只是部分请求的处理会比较慢,在压测工具中的表现就是平均响应时间变长;当每秒请求数大于20时,超出了burst设定的缓存大小,开始有请求被拒绝,平均响应时间仍然较长。

    • 假设 rate=10r/s,burst=20,加了nodelay,当每秒请求数大于10,小于20时,会有部分请求被拒绝,在压测工具中的表现是平均响应时间不变;当每秒请求数大于20时,同样如此,不过被拒绝率相应提高。

    综合测试结果来看,不管前面怎么设置,服务器的处理能力,基本都是 10r/s ,只是响应时间不同,nodelay并不能提高服务器处理能力。

如何设置

这里提供一个参考思路:首先使用压测工具在不启用限流时,测出来服务器的最大处理能力,作为 rate 的值,把 burst 设置为 rate 的两倍,同时加上nodelaypm.max_children 设置为与 burst 一样的值。

服务器的处理能力是有限的,不是一味的增加 max_children,就能够处理更多的请求,加大了 max_children,RPS不会怎么变化,但是平均响应时间会变长。因此 rate 就设置为最大处理能力,不浪费服务器资源,同时万一偶尔遇上突发流量,也能处理过来,只是响应时间变长了,而 pm.max_children = burst 可以确保同时被处理的请求数不会超过php的承受力。

你可能感兴趣的:(关于php-fpm最大连接数和nginx限流)