8.6 KiB
8.6 KiB
PHP 连接泄漏问题修复
问题描述
Python 后端调用 PHP 后端接口时出现超时错误:
HTTPConnectionPool(host='192.168.1.164', port=30100): Read timed out. (read timeout=5)
问题根源
通过 netstat -ano | findstr :30100 检查发现:
- PHP 服务(PID 23736 和 1416)有 30+ 个
CLOSE_WAIT连接 CLOSE_WAIT状态表示:客户端已关闭连接,但服务器端未关闭- 这是典型的连接泄漏问题
为什么会出现 CLOSE_WAIT?
-
PHP 内置开发服务器的限制
php -S是单线程服务器,设计用于开发测试- 在处理大量并发请求时容易出现连接泄漏
- 长时间运行会导致资源耗尽
-
连接未正确关闭
- 客户端(Python)发送请求后关闭连接
- 服务器端(PHP)没有正确关闭 socket
- 连接进入 CLOSE_WAIT 状态并一直保持
-
资源耗尽
- 大量 CLOSE_WAIT 连接占用系统资源
- 导致新请求无法处理或响应缓慢
- 最终导致超时错误
临时解决方案:重启 PHP 服务
方法 1:使用快速重启脚本(推荐)
双击运行 restart_php_service.bat:
restart_php_service.bat
这个脚本会:
- 检查当前 PHP 服务状态
- 停止所有 PHP 服务进程
- 等待端口释放
- 启动新的 PHP 服务
方法 2:手动重启
# 1. 查看当前 PHP 进程
netstat -ano | findstr :30100
# 2. 停止所有 PHP 进程(替换 PID)
taskkill /F /PID 23736
taskkill /F /PID 1416
# 3. 等待 2 秒
# 4. 启动新的 PHP 服务
cd C:\Users\Administrator\Desktop\Project\AI_GirlFriend\xunifriend_RaeeC\public
php -S 192.168.1.164:30100
验证服务已重启
# 检查服务状态
netstat -ano | findstr :30100
# 应该只看到 LISTENING 状态,没有 CLOSE_WAIT
监控连接状态
使用监控脚本
双击运行 monitor_php_connections.bat:
monitor_php_connections.bat
这个脚本会每 5 秒刷新一次,显示:
- 所有连接状态
- ESTABLISHED 连接数(正常活跃连接)
- CLOSE_WAIT 连接数(连接泄漏)
- TIME_WAIT 连接数(正常关闭中)
判断标准
- 正常:CLOSE_WAIT < 10 个
- 注意:CLOSE_WAIT 10-20 个(需要关注)
- 警告:CLOSE_WAIT > 20 个(建议立即重启)
长期解决方案
方案 1:使用 Nginx + PHP-FPM(推荐)
PHP 内置服务器不适合生产环境,建议使用 Nginx + PHP-FPM。
安装步骤
-
下载 Nginx for Windows
- 访问:https://nginx.org/en/download.html
- 下载稳定版(Stable version)
-
下载 PHP(非线程安全版本)
- 访问:https://windows.php.net/download/
- 下载 NTS (Non Thread Safe) 版本
-
配置 PHP-FPM
创建 php-cgi.bat:
@echo off
cd C:\php
php-cgi.exe -b 127.0.0.1:9000
- 配置 Nginx
编辑 nginx.conf:
server {
listen 30100;
server_name 192.168.1.164;
root C:/Users/Administrator/Desktop/Project/AI_GirlFriend/xunifriend_RaeeC/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
- 启动服务
# 启动 PHP-FPM
start php-cgi.bat
# 启动 Nginx
cd C:\nginx
start nginx.exe
优点
- 支持多进程,性能更好
- 连接管理更稳定
- 适合生产环境
- 不会出现连接泄漏
方案 2:定期自动重启 PHP 服务
如果暂时无法切换到 Nginx,可以设置定时任务自动重启 PHP 服务。
创建定时任务
- 打开"任务计划程序"(Task Scheduler)
- 创建基本任务
- 设置触发器:每 4 小时
- 操作:启动程序
restart_php_service.bat
或使用 Windows 计划任务命令
schtasks /create /tn "重启PHP服务" /tr "C:\path\to\restart_php_service.bat" /sc hourly /mo 4
方案 3:优化 Python 请求代码
在 lover/deps.py 中优化 HTTP 请求:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 创建带重试和连接池的 session
def get_http_session():
session = requests.Session()
# 配置重试策略
retry = Retry(
total=3,
backoff_factor=0.3,
status_forcelist=[500, 502, 503, 504]
)
# 配置连接池
adapter = HTTPAdapter(
max_retries=retry,
pool_connections=10,
pool_maxsize=20,
pool_block=False
)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
# 使用 session
def _fetch_user_from_php(token: str) -> Optional[dict]:
"""通过 PHP/FastAdmin 接口获取用户信息。"""
import logging
logger = logging.getLogger(__name__)
user_info_api = "http://192.168.1.164:30100/api/user_basic/get_user_basic"
logger.info(f"用户中心调试 - 调用接口: {user_info_api}")
try:
session = get_http_session()
resp = session.get(
user_info_api,
headers={
"token": token,
"Connection": "close" # 明确关闭连接
},
timeout=10, # 增加超时时间
)
logger.info(f"用户中心调试 - 响应状态码: {resp.status_code}")
# 确保连接关闭
resp.close()
except requests.exceptions.Timeout:
logger.error(f"用户中心调试 - 请求超时")
raise HTTPException(
status_code=status.HTTP_504_GATEWAY_TIMEOUT,
detail="用户中心接口超时",
)
except Exception as exc:
logger.error(f"用户中心调试 - 请求异常: {exc}")
raise HTTPException(
status_code=status.HTTP_502_BAD_GATEWAY,
detail="用户中心接口不可用",
) from exc
# ... 其余代码
预防措施
1. 监控连接状态
定期运行 monitor_php_connections.bat 检查连接状态。
2. 设置告警
当 CLOSE_WAIT 连接数超过阈值时,发送告警通知。
3. 日志记录
在 Python 代码中记录每次 PHP 调用的耗时:
import time
start_time = time.time()
resp = requests.get(...)
elapsed_time = time.time() - start_time
logger.info(f"PHP 接口调用耗时: {elapsed_time:.2f}秒")
if elapsed_time > 3:
logger.warning(f"PHP 接口响应缓慢: {elapsed_time:.2f}秒")
4. 健康检查
添加健康检查端点,定期检查 PHP 服务状态:
@app.get("/health/php")
async def check_php_health():
try:
resp = requests.get(
"http://192.168.1.164:30100/api/health",
timeout=2
)
return {
"status": "healthy" if resp.status_code == 200 else "unhealthy",
"response_time": resp.elapsed.total_seconds()
}
except:
return {"status": "down"}
常见问题
Q1: 为什么会有两个 PHP 进程(PID 23736 和 1416)?
A: 可能是之前启动了多次 PHP 服务,导致有多个进程在监听同一端口。建议:
- 停止所有 PHP 进程
- 只启动一个 PHP 服务
Q2: 重启后还是有 CLOSE_WAIT 怎么办?
A:
- 确认已停止所有旧的 PHP 进程
- 检查是否有其他程序占用端口
- 考虑更换端口或使用 Nginx
Q3: 如何判断是 PHP 问题还是 Python 问题?
A:
- 使用 curl 直接测试 PHP 接口:
curl -X GET "http://192.168.1.164:30100/api/user_basic/get_user_basic" -H "token: YOUR_TOKEN" - 如果 curl 正常,说明是 Python 客户端问题
- 如果 curl 也慢,说明是 PHP 服务器问题
Q4: 生产环境应该用什么?
A:
- 不推荐:
php -S(仅用于开发) - 推荐:Nginx + PHP-FPM
- 备选:Apache + mod_php
快速参考
检查连接状态
netstat -ano | findstr :30100
重启 PHP 服务
restart_php_service.bat
监控连接
monitor_php_connections.bat
停止所有服务
stop_all_services.bat
启动所有服务
start_all_services.bat
总结
- 问题根源:PHP 内置服务器连接泄漏
- 临时方案:定期重启 PHP 服务
- 长期方案:使用 Nginx + PHP-FPM
- 监控措施:使用监控脚本定期检查
建议尽快切换到 Nginx + PHP-FPM,彻底解决连接泄漏问题!