Ai_GirlFriend/开发/2026年2月3日/PHP连接泄漏问题修复.md
2026-02-04 18:47:56 +08:00

386 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
1. **PHP 内置开发服务器的限制**
- `php -S` 是单线程服务器,设计用于开发测试
- 在处理大量并发请求时容易出现连接泄漏
- 长时间运行会导致资源耗尽
2. **连接未正确关闭**
- 客户端Python发送请求后关闭连接
- 服务器端PHP没有正确关闭 socket
- 连接进入 CLOSE_WAIT 状态并一直保持
3. **资源耗尽**
- 大量 CLOSE_WAIT 连接占用系统资源
- 导致新请求无法处理或响应缓慢
- 最终导致超时错误
---
## 临时解决方案:重启 PHP 服务
### 方法 1使用快速重启脚本推荐
双击运行 `restart_php_service.bat`
```batch
restart_php_service.bat
```
这个脚本会:
1. 检查当前 PHP 服务状态
2. 停止所有 PHP 服务进程
3. 等待端口释放
4. 启动新的 PHP 服务
### 方法 2手动重启
```bash
# 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
```
### 验证服务已重启
```bash
# 检查服务状态
netstat -ano | findstr :30100
# 应该只看到 LISTENING 状态,没有 CLOSE_WAIT
```
---
## 监控连接状态
### 使用监控脚本
双击运行 `monitor_php_connections.bat`
```batch
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。
#### 安装步骤
1. **下载 Nginx for Windows**
- 访问https://nginx.org/en/download.html
- 下载稳定版Stable version
2. **下载 PHP非线程安全版本**
- 访问https://windows.php.net/download/
- 下载 NTS (Non Thread Safe) 版本
3. **配置 PHP-FPM**
创建 `php-cgi.bat`
```batch
@echo off
cd C:\php
php-cgi.exe -b 127.0.0.1:9000
```
4. **配置 Nginx**
编辑 `nginx.conf`
```nginx
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;
}
}
```
5. **启动服务**
```batch
# 启动 PHP-FPM
start php-cgi.bat
# 启动 Nginx
cd C:\nginx
start nginx.exe
```
#### 优点
- 支持多进程,性能更好
- 连接管理更稳定
- 适合生产环境
- 不会出现连接泄漏
### 方案 2定期自动重启 PHP 服务
如果暂时无法切换到 Nginx可以设置定时任务自动重启 PHP 服务。
#### 创建定时任务
1. 打开"任务计划程序"Task Scheduler
2. 创建基本任务
3. 设置触发器:每 4 小时
4. 操作:启动程序 `restart_php_service.bat`
#### 或使用 Windows 计划任务命令
```batch
schtasks /create /tn "重启PHP服务" /tr "C:\path\to\restart_php_service.bat" /sc hourly /mo 4
```
### 方案 3优化 Python 请求代码
`lover/deps.py` 中优化 HTTP 请求:
```python
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 调用的耗时:
```python
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 服务状态:
```python
@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 服务,导致有多个进程在监听同一端口。建议:
1. 停止所有 PHP 进程
2. 只启动一个 PHP 服务
### Q2: 重启后还是有 CLOSE_WAIT 怎么办?
**A**:
1. 确认已停止所有旧的 PHP 进程
2. 检查是否有其他程序占用端口
3. 考虑更换端口或使用 Nginx
### Q3: 如何判断是 PHP 问题还是 Python 问题?
**A**:
1. 使用 curl 直接测试 PHP 接口:
```bash
curl -X GET "http://192.168.1.164:30100/api/user_basic/get_user_basic" -H "token: YOUR_TOKEN"
```
2. 如果 curl 正常,说明是 Python 客户端问题
3. 如果 curl 也慢,说明是 PHP 服务器问题
### Q4: 生产环境应该用什么?
**A**:
- **不推荐**`php -S`(仅用于开发)
- **推荐**Nginx + PHP-FPM
- **备选**Apache + mod_php
---
## 快速参考
### 检查连接状态
```bash
netstat -ano | findstr :30100
```
### 重启 PHP 服务
```bash
restart_php_service.bat
```
### 监控连接
```bash
monitor_php_connections.bat
```
### 停止所有服务
```bash
stop_all_services.bat
```
### 启动所有服务
```bash
start_all_services.bat
```
---
## 总结
1. **问题根源**PHP 内置服务器连接泄漏
2. **临时方案**:定期重启 PHP 服务
3. **长期方案**:使用 Nginx + PHP-FPM
4. **监控措施**:使用监控脚本定期检查
建议尽快切换到 Nginx + PHP-FPM彻底解决连接泄漏问题