PHP的session文件锁引发的问题
问题描述
最近做了个测试接口,专门用于测试主程序目录下接口的响应速度,目录结构如下:
pro:主程序目录
|---server:用于存放各种后端服务
|--web_api
|-someApi
|- common.php
|-someApi
|-...
|-user_info.php
|-_check_login.php
|---index.html
|--- ...
|---api-test:测试程序
|--someApi
|--...
|-- test_common.php
主程序中的代码
在web_api/someApi/common.php 中引入了 外层的user_info个文件:
include_once (dirname(__DIR__) . '/user_info.php');
然后user_info则引入了 ——_check_login.php
include_once (dirname(__DIR__) . '/server/_check_login.php');
// session_start() 写在这里也不行!!
_check_login.php的代码如下所示:
<?php
session_start();
if ( $isLogin ) {
$resultLogin = array(
...
);
echo json_encode($resultLogin, JSON_UNESCAPED_UNICODE);
exit;
} else {
...
}
?>
测试程序中的代码
再来看测试程序中的代码,api-test/test_common.php:
<?php
ini_set('date.timezone','Asia/Shanghai');
ini_set('max_execution_time', '120');
set_time_limit(120);
sleep(10);
// 启动session会话
session_start();
问题描述
然后,我先用浏览器访问api-test/someApi/app.php的时候,由于添加了sleep,会发现请求一直在处理中。此时,我再用浏览器打开pro/index.html,会发现在pro/index.html中的请求也一同处于等待!如下图:
问题解答
导致这个问题出现的根本原因,就是session_start!!
会话文件锁定机制
PHP 默认使用文件来存储会话数据,当调用session_start()
时,PHP 会尝试对会话文件加锁,以确保同一时间只有一个请求可以修改该会话数据。如果多个请求尝试同时访问同一个会话,后面的请求会被阻塞,直到前面的请求释放锁。
假设有一个 PHP 页面test.php
<?php
session_start();
// 模拟一个耗时操作
sleep(10);
echo "Process finished.";
?>
当你同时打开两个浏览器窗口访问test.php
时,第二个请求会被阻塞,直到第一个请求执行完毕并释放会话锁。
所以最终问题原因就是api-test/test_common.php中的session_start()!!把这里的session_start去掉就一切正常了!!
当然也可以用其他方式解决这个问题:
缩短会话锁定时间
尽量减少在会话锁定期间执行的耗时操作。例如,将一些不必要的操作放在session_start()
之前或之后执行。
// 执行一些不依赖会话的操作
// ...
session_start();
// 执行依赖会话的操作
// ...
// 关闭会话,释放锁
session_write_close();
// 继续执行其他操作
// ...
使用数据库存储会话数据
// 自定义会话处理函数
class DatabaseSessionHandler implements SessionHandlerInterface {
// 实现接口中的方法
// ...
}
// 注册自定义会话处理函数
$handler = new DatabaseSessionHandler();
session_set_save_handler($handler, true);
// 启动会话
session_start();
资源竞争问题
如果服务器资源有限,例如磁盘 I/O、内存等,多个请求同时调用session_start()
可能会导致资源竞争,从而出现阻塞现象。
- 优化服务器配置:增加服务器的硬件资源,如增加内存、更换高速磁盘等,以提高服务器的处理能力。
- 使用缓存:对于一些频繁访问的数据,可以使用缓存技术(如 Redis、Memcached)来减少对会话文件或数据库的访问,从而减轻服务器的负担。