PHP自定义会话清理函数触发unlink文件不存在警告的排查求助
问题原因
生产环境下php-fpm是多进程模式,多个请求会同时执行自定义会话清理函数,这会触发竞态条件:
- 进程A通过
glob获取到会话文件列表,包含文件X - 进程A执行
file_exists($file)时返回true,但在调用unlink($file)之前,进程B(或PHP自身的会话GC进程)已经删除了文件X - 进程A执行
unlink时文件已不存在,因此触发警告
另外,PHP默认的会话垃圾回收机制(session.gc_probability默认不为0)会随机启动清理逻辑,也会和自定义清理逻辑产生冲突,进一步增加报错概率。
解决方案
1. 给unlink添加错误抑制
既然文件不存在属于并发场景下的预期情况,可以直接抑制该警告:
@unlink($file);
2. 删除前再次检查文件状态
在执行unlink前再次验证文件存在性,能大幅降低报错概率:
if ($now - $mfile_time >= SESSION_EXPIRE_SECONDS) { // 再次确认文件是普通文件且存在 if (is_file($file)) { unlink($file); } }
3. 禁用PHP默认会话GC
由于已经实现自定义清理逻辑,建议在php.ini或.user.ini中关闭默认GC,避免冲突:
session.gc_probability = 0
4. 改用定时任务清理(推荐)
不在每个请求中执行清理逻辑,改用系统定时任务定期处理,从根源避免并发问题:
- 编写独立清理脚本
clean_sessions.php:
define("SESSION_SAVE_PATH", "/tmp/my_php_sessions"); define("SESSION_EXPIRE_SECONDS", 36000); if (!is_dir(SESSION_SAVE_PATH)) { mkdir(SESSION_SAVE_PATH, 0777, true); } $files = glob(SESSION_SAVE_PATH . '/sess_*'); $now = time(); foreach ($files as $file) { if (is_file($file) && ($now - filemtime($file) >= SESSION_EXPIRE_SECONDS)) { @unlink($file); } }
- 添加cron任务,每小时执行一次:
0 * * * * php /path/to/clean_sessions.php
额外排查建议
开发环境无法复现是因为并发量不足,可使用压力测试工具(如ab、wrk)模拟多请求并发,触发竞态条件验证问题。
内容的提问来源于stack exchange,提问作者Sbraaa
相关产品推荐
相关产品推荐

