Nginx+PHP环境AJAX调用超500次后服务无响应求助
我正在开发一个定期从Web服务器拉取数据的简单页面,采用Nginx+PHP+AJAX的技术方案。页面初期运行正常,但在发起500次AJAX调用后,Web服务器彻底停止响应——PHP直接崩溃了,我一直找不到问题根源。
可复现的极简代码
前端页面(ajaxBug.html)
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Ajax Call demos</title> <script language="javascript" type="text/javascript" src="/flot/jquery.js"></script> <script type="text/javascript"> $(function() { var totalCounter = 0; function fetchData() { $.ajax({ type: 'POST', url: 'dynamicData.php', success: function(data) { $("#result").html("<p>Ajax Calls : " + totalCounter + "<br/></p>"); totalCounter++; }, dataType: 'text', }); setTimeout(fetchData, 10); }; fetchData(); }); </script> </head> <body> <div id="result"> result </div> </body> </html>
后端PHP(dynamicData.php)
<?php echo 1; ?>
Nginx错误日志
upstream timed out (10060: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond) while reading response header from upstream, client: 127.0.0.1, server: localhost, request: "POST /flot/essai/dynamicData.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "127.0.0.1", referrer: "http://127.0.0.1/flot/essai/ajaxBug.html"
我尝试过调整各种超时参数,但问题依旧,还是会在500次调用后崩溃。想请教各位,这个问题的成因是什么?该如何解决?
问题核心原因
你碰到的问题本质是PHP-FPM的进程池被彻底耗尽了。默认情况下,PHP-FPM的pm.max_children参数(控制最大可同时运行的PHP进程数)通常设置在500左右。你的前端代码是每10ms就发起一次AJAX请求,而且完全不等待上一次请求完成就立刻发起下一次——这会导致大量并发请求同时占用PHP-FPM的进程,当进程数达到pm.max_children的上限时,新的请求只能排队等待,直到超时,最终表现为服务器无响应。刚好500次触发崩溃,就是因为触碰到了这个默认的进程数上限。
解决方案(从易到难)
1. 前端代码修复:避免请求堆积
这是最快速有效的解决方式,只需要修改前端逻辑,确保上一次AJAX请求完成(成功或失败)后,再发起下一次请求,而不是不管不顾地定时发起。这样能保证同一时间最多只有一个请求在处理,不会瞬间耗尽PHP进程:
$(function() { var totalCounter = 0; function fetchData() { $.ajax({ type: 'POST', url: 'dynamicData.php', success: function(data) { $("#result").html("<p>Ajax Calls : " + totalCounter + "<br/></p>"); totalCounter++; // 上一次请求成功后,再延迟10ms发起下一次 setTimeout(fetchData, 10); }, dataType: 'text', // 加上错误处理,避免请求失败后中断循环 error: function() { // 请求失败后可以延迟久一点再重试,避免频繁重试加重服务器负担 setTimeout(fetchData, 1000); } }); }; fetchData(); });
2. 后端PHP-FPM配置调整
如果你的业务确实需要更高的并发量,可以修改PHP-FPM的配置文件(通常路径为/etc/php/[你的PHP版本]/fpm/pool.d/www.conf):
pm.max_children:根据服务器的CPU和内存情况适当调整,比如4核8G的服务器可以设置为100-200(注意不要过高,否则会占用过多内存导致服务器崩溃)pm.start_servers:设置PHP-FPM启动时初始化的进程数,建议设为pm.max_children的1/4pm.min_spare_servers和pm.max_spare_servers:控制空闲进程的数量范围,避免频繁创建销毁进程消耗资源
修改完成后,重启PHP-FPM服务生效:
sudo systemctl restart php[你的PHP版本]-fpm
3. 架构优化:用长连接替代高频轮询
如果你的需求是实时获取数据,高频短轮询并不是最优方案,推荐使用Server-Sent Events(SSE)或者WebSocket,让服务器主动推送数据给前端,这样只需要一个长连接就能持续获取数据,从根本上避免大量短请求耗尽进程的问题。
举个SSE的例子:
后端PHP(dynamicData.php)
<?php // 设置SSE响应头 header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); $counter = 0; while (true) { // 推送数据给前端 echo "data: {$counter}\n\n"; // 强制输出缓冲区内容 ob_flush(); flush(); $counter++; // 模拟10ms间隔 usleep(10000); // 检测客户端是否断开连接,避免无效进程占用 if (connection_aborted()) { break; } }
前端代码
var source = new EventSource('dynamicData.php'); var totalCounter = 0; source.onmessage = function(event) { $("#result").html("<p>Ajax Calls : " + totalCounter + "<br/></p>"); totalCounter++; }; // 监听连接错误 source.onerror = function() { console.error('SSE连接出错,尝试重连'); setTimeout(function() { source = new EventSource('dynamicData.php'); }, 1000); };
内容的提问来源于stack exchange,提问作者mazipha

