【PHP】从服务器负载一路走到PHP-FPM优化终于搞定了PHP项目偶尔很慢的问题
原文链接https://zhuanlan.zhihu.com/p/681575918
项目情况: 阿里云Rds
阿里云Redis PHP8 IN Docker
本文会从几个方面来讲PHP线上项目怎么进行性能瓶颈定位,以及优化方法。
前段时间有个PHP项目开始出现偶尔有一些请求会超时,最长的请求可能需要长达20S
。 一般出现这总情况的时候,我们首先想到的会不会服务器负载不够,或者个某个查询花了太多时间。
观察服务器负载
服务器负载 30% 左右,一切正常
数据库负载 60% 左右,一切正常
Redis负载 30% 左右,一切正常
首先排除了是因为服务器负载的原因,那么接下来想到的就是 Mysql 部分查询很慢导致的。
检查数据库负载以及 Sql慢查询
数据库负载一切正常,当看到这个情况的时候我们心里其实排除了是数据库的原因了。
继续查看 Sql慢日志,发现有一些日志,都是1-2 秒之间,没有什么特别需要关心的。
这里也排除了数据库的原因,接下来继续查看 Nginx情况。
检查 Nginx 日志
这里检查 Nginx 日志主要是查询是否又Http
状态码 是429
这样的请求,如有出现很频繁,那么表示是 Nginx Worker
不够,或者是 PHP程序内部返回的 429
。其实我们这次的原因可以排除是这个可能,因为如果是这个问题,那么那么请求不应该很慢,应该会直接返回错误码。那么当我们遇到Nginx
返回429
我们一般怎么处理呢。
429 (Too Many Requests)
首先我们要知道什么时候 Nginx
会返回这个错误码:在Nginx服务器中遇到HTTP状态码429(Too Many Requests)时,通常意味着客户端在短时间内发送了过多的请求,超出了服务器允许的速率限制。
常用解决方案
一、调整 Nginx worker_processes
和worker_connections
首先检查worker_processes
和worker_connections
看看设置的大小是多少
worker_processes
它定义了Nginx要启动的工作进程(worker process)的数量。工作进程是实际处理客户端请求的核心单元,负责接收和处理HTTP请求、读写磁盘上的文件以及与上游服务器通信等任务。通过设置多个工作进程,Nginx可以充分利用多核CPU的优势,分散负载到不同的核心上并行处理请求,从而提高系统的并发处理能力和整体性能。
自动检测CPU核心数,并设置相应数量的工作进程(推荐)
worker_processes auto;
如果你的服务器是4核CPU或多核CPU但希望限制为使用4个核心,当然我们通常可以设置到 2-3 倍,比如 8 或者 12.
worker_processes 4;
worker_connections
是 Nginx 服务器配置中的一个重要参数,它定义了每个工作进程(worker process)可以同时处理的最大并发连接数。这个设置直接影响到Nginx服务器能够同时服务的客户端请求数量。每一个工作进程都有一个最大连接数的限制,超过这个限制后,新的连接将无法被接收和处理。合理设置 worker_connections
可以有效利用系统资源,避免因并发连接数过大导致内存溢出或其他资源耗尽问题。
worker_rlimit_nofile 10240;
events {
use epoll; # 对于Linux环境,推荐使用epoll事件模型
multi_accept on; # 允许一个工作进程接受多个连接
worker_connections 6144; # 每个工作进程允许的最大并发连接数
}
这里设置的是每个工作进程可同时处理的并发连接数为6144。实际应用中,根据服务器硬件性能、操作系统参数(如打开文件描述符数量上限)、以及预期业务负载进行调整。另外,在设置 worker_connections
参数时,需要确保其值小于或等于 worker_rlimit_nofile
参数设置的值(即每个工作进程允许打开的最大文件描述符数量),因为每个网络连接都需要占用一个文件描述符。 worker_rlimit_nofile
指令用于设置每个工作进程可以打开的最大文件描述符数量,这对于处理高并发连接至关重要。当Nginx作为Web服务器或反向代理时,它需要为每个活动连接保持一个打开的文件描述符(例如套接字)。如果这个限制过低,可能会导致无法处理更多的客户端连接,即使系统资源充足。 那么如何设置worker_rlimit_nofile
,对于worker_rlimit_nofile
的设置我们可以参考系统自身最大打开文件数。
ulimit -n
65535
这里最大为 65535 所以我们可以设置为65535 .但是如果直接把worker_rlimit_nofile
设置到65535,如果请求过大的时候可能就会影响其他的进程。
二、限速限流
可以对某一个 IP 进行限流限速,这样可以排除一些异常的请求。也可以保障其他请求的正常执行。
二、负载均衡与扩展性考虑:
如果是因为服务器处理能力达到瓶颈,可以考虑负载均衡技术,将流量分散到多个服务器上,或者根据需要扩展服务器资源。
检查PHP-FPM
当服务器,数据库,Nginx
都正常之后,我就想到最后的一个可能,PHP-FPM
进程不能够用了。 查看 Docker 默认的FPM进程配置
cat /usr/local/etc/php-fpm.d/www.conf
pm = dynamic
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 5
; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: (min_spare_servers + max_spare_servers) / 2
pm.start_servers = 2
发现默认只有 最大只有 5 个进程,所以我们增大到 20 个,修改配置如下
pm = dynamic
pm.start_servers = 5
pm.max_children = 20
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 500
pm.process_idle_timeout = 10s
由于这里使用的Docker
,所以我们尽量使用挂载配置文件的方式来修改配置,使用Docker-compose
来挂载新增的php配置zz-docker.conf
volumes:
- "./docker/php/zz-docker.conf:/usr/local/etc/php-fpm.d/zz-docker.conf:cached"
最后重启Docker,通过一周观察,一切回到正常状态。
最后:常用的PHP-FPM各个参数的含义以及配置
PHP-FPM(参数优化是提高服务器性能和稳定性的关键步骤之一,以下是一些主要的PHP-FPM配置参数及其优化建议:
进程管理方式 (
**pm**
):
pm = static
: 静态模式,固定数量的子进程。
pm.max_children = N # 设置最大子进程数
pm = dynamic
: 动态模式,根据负载自动调整子进程数量。
pm.max_children = N # 最大子进程数
pm.start_servers = M # 启动时创建的进程数
pm.min_spare_servers = X # 空闲时最小进程数
pm.max_spare_servers = Y # 空闲时最大进程数
子进程数量 (
**max_children**
): 根据服务器内存大小和单个PHP请求的平均内存消耗计算出合适的值。确保所有子进程占用的总内存不超过服务器物理内存的75%左右并需要根据实际情况调整,以免出现内存溢出。请求处理超时 (
**request_terminate_timeout**
):
request_terminate_timeout = T # 请求处理超时时间(秒)
设定一个合理的脚本执行超时时间,防止因个别耗时过长的请求导致资源浪费或服务卡顿。
监听队列 (
**listen.backlog**
):
这个参数决定了等待连接队列的最大长度,可以根据系统负载和并发需求进行适当调整。
错误日志级别 (
**log_level**
):
log_level = notice # 或 error, warning, debug等
选择适当的日志级别以平衡调试需要与减少日志输出对性能的影响。
慢日志记录 (
**slowlog**
):
slowlog = /path/to/slow.log
request_slowlog_timeout = S # 超过多少秒的请求会被记录到慢日志
记录执行时间过长的请求,帮助分析和优化代码。
每个参数的优化都需要结合实际情况,如服务器硬件资源、网站访问量特点以及PHP应用程序的具体行为等因素综合考虑,并且可能需要反复测试和调优。
猜你喜欢
- 【PHP】PHP静态化
- 随着互联网的快速发展,网站访问量越来越大,同时网站的性能也成为了设计者们需要考虑的重要问题之一。对于使用php语言的开发人员来说,静态化是提高网站性能的一种有效方法。在本文中,我们将探讨php中静态化的方法。一、什么是静态化静态化是指将动态生成的网页文件(如PHP文件)转换为静态的HTML文件存储在服务器上。当用户请求时,直接访问静态HTML文件,避免了每次请求都要执行PHP代码的情况,从而提高了网站的性能响应速度。二、静态化的优劣静态化虽然能够提高网站响应速度,但它也存在一些缺陷:优点:1.减
- 【PHP】php如何创建关联数组表格
- 随着互联网技术的不断发展,Web 应用程序的开发变得越来越重要。其中,关联数组表格是 Web 应用程序中常用的一种数据结构,它可以将数据按照列与行的方式分组存储,并且可以方便地在前端界面中进行展示。那么,如何使用 PHP 创建关联数组表格呢?本文将为您一一介绍。一、创建关联数组在 PHP 中,我们可以使用关联数组来存储数据,关联数组是以字符串为索引的数组。相较于索引数组(使用数字作为索引的数组),关联数组更加灵活,可以通过索引来直接访问每个元素。下面的示例代码创建了一个关联数组,包含了
- 【PHP】PHP防止XSS攻击的主流方法
- 概述跨站点脚本 (XSS) 是一种严重的安全漏洞,允许恶意行为者将恶意脚本引入网站,使毫无戒心的访问者处于危险之中。使用 XSS,攻击者可以在受害者的 Web 浏览器中执行任意代码,可能导致敏感数据被盗、未经授权的访问或网站污损。本文旨在深入探讨 XSS 攻击的主要形式,阐明其根本原因,探索 XSS 利用的潜在后果,并深入了解防止 PHP 中 XSS 攻击的有效措施。介绍当恶意行为者成功将有害脚本插入受信任的网站时,就会发生跨站脚本 (XSS) 攻击。这些受感染的网站在不知不
- 【PHP】PHP8如何通过Sanitize Filters来增强应用程序的安全性
- PHP是一门广泛应用于Web开发的脚本语言,而安全性一直是Web应用程序开发者需要关注的重要问题。PHP8提供了一种称为Sanitize Filters的机制,通过对用户输入进行过滤和清理,可以增强应用程序的安全性。本文将详细介绍PHP8中Sanitize Filters的使用方法,并提供一些具体的代码示例,帮助开发者更好地了解如何应用这一特性。首先,让我们来了解一下Sanitize Filters是什么。Sanitize Filters是一组用于过滤和清理用户输入数据的PHP函数,可以帮助开发
- 【PHP】PHP8如何优化代码性能
- PHP8的新特性和底层开发原理探索:如何优化代码性能随着互联网的迅猛发展和信息技术的不断更新,PHP作为一门重要的互联网开发语言也在不断演进。PHP8作为最新版本,带来了许多全新的特性和改进,同时也提供了更强大的底层开发能力。本文将探讨PHP8的新特性,并给出一些代码示例,展示如何优化代码性能。JIT编译器PHP8引入了Just-In-Time(即时编译)编译器。JIT编译器可以将PHP代码动态地编译成本地机器码,从而提高代码执行的效率。在PHP8中,JIT编译器默认是关闭的,需要手动启用。下面
- 【PHP】PHP中使用ElasticSearch
- 在es中,使用组合条件查询是其作为搜索引擎检索数据的一个强大之处,在前几篇中,简单演示了es的查询语法,但基本的增删改查功能并不能很好的满足复杂的查询场景,比如说我们期望像mysql那样做到拼接复杂的条件进行查询该如何做呢?es中有一种语法叫bool,通过在bool里面拼接es特定的语法可以做到大部分场景下复杂条件的拼接查询,也叫复合查询首先简单介绍es中常用的组合查询用到的关键词,filter:过滤,不参与打分 must:如果有多个条件,这些条件都必须满足 and与 should:如果有多个条
- 【PHP】php替换字符串
- 在PHP中,字符串替换是常见的操作,而替换所有匹配的字符串更是常见需求。本文将详细介绍如何使用PHP中的替换函数来替换所有匹配字符串。一、str_replace函数PHP中最常用的替换函数是str_replace()函数,它可以替换一个字符串中的指定部分。其语法如下:string str_replace(mixed $search, mixed $replace, mixed $subject[, int
- 【PHP】intervention/image设置文字竖排显示
- 在使用 intervention/image 库时,要让文字竖排显示,可以通过设置文字的样式和使用 rotate 方法将文字旋转90度来实现。以下是一个示例代码:use Intervention\Image\ImageManagerStatic as Image; // 初始化ImageManager $imageManager = new Image(); // 加载背景图片 $back