使用默认会话处理程序时,有没有办法在PHP中禁用会话锁定?
[编辑:]或者至少有一种方法可以在调用session_write_close()
后重启会话?如果已经将任何输出发送到浏览器,则session_start()
不起作用。
答案 0 :(得分:16)
你不想禁用它...如果你这样做,你可能会遇到各种奇怪的问题,你在一个窗口登录,登录到另一个窗口,然后结束不一致的状态。 ..锁定是有原因的......
相反,如果您知道不打算在该请求中写入会话,请尽早关闭会话。一旦你启动它,即使你打电话给session_write_close
,你也可以从中读取整个请求(除非你重新启动它,或做一些其他特殊的事情)。那么你要做的是检查请求是否是写请求,如果不是,请在打开后立即关闭它。请注意,如果您稍后尝试写入会话(对于Captcha或CSRF保护或其他内容),这可能会产生一些不利影响,因此请谨慎使用...
但是,我不会试图绕过它,而是努力缩短请求长度(以减少锁争用),或者对那些根本不需要会话的请求进行无cookie请求...... / p>
答案 1 :(得分:11)
我有一个超过2分钟的报告页面(超过80个AJAX请求)。我通过删除会话锁定将其降低到30秒以下。 是的,天堂禁止你删除文件锁定,因为那时你有竞争条件。如果你不了解竞争条件,你就不知道这对你的影响是什么会话...然后不要删除文件锁定。但是,如果你知道你的会话中有什么数据,并且知道什么样的竞争条件,那么就会觉得没有数据可能会受到种族条件的不利影响而导致错误...你知道你的环境比其他人都这样做,所以去吧。
MySQL,REDIS和Memcache
另外,请注意,如果您切换到MySQL进行会话管理,那么99%的机会,恕我直言,您没有锁定从您阅读的时间到写入时间的行。因此,使用MySQL,您仍然具有相同的竞争条件(如果您决定锁定该行,则会出现锁定问题)。
根据我能找到的信息,使用PHP Redis的人正在使用一个容易出现竞争条件的非锁定应用......按照以下主题...他们引用速度作为他们的原因之一像这样"功能":
https://github.com/phpredis/phpredis/issues/37
Memcached并没有支持会话锁定,直到版本3.0.4 ......所以它最初也很容易出现竞争条件。
显然,随着这些选项的成功,竞争条件并不是程序员面临的最大问题。
最终问题
所有并发请求将始终受竞争条件 UNLESS 进行文件锁定,此时他们 不再是并发请求。
关于Sessions和Locking vs. Concurrency以及竞争条件的重要之处在于了解您的应用程序,了解竞争条件是否会破坏您的应用程序......并设计适合您应用程序的解决方案。如果您只是将userId存储在会话中并在所有后续请求中读取它,您可能不必担心竞争条件。如果您存储精心设计的数据历史记录,如果事情发生故障或数据可能会丢失,则会中断数据,然后在读取和写入之间锁定文件并尝试在读取后尽快写入以限制时间文件被锁定。
最佳选择
然而,无会话API对于并发请求会好得多。但是,如果您没有时间重构这样的API,那么请继续阅读。
停止使用PHP会话文件并停止锁定的Stop-Gap解决方案
要继续使用PHP会话,默认方式,停止锁定,并且基本上可以快速解决复杂问题,您可以实现PHP网站的SessionHandler示例实现。
我在生产环境中为每分钟有数万个连接的网站运行以下代码,但我还没有遇到任何竞争条件问题,但我也没有存储数据竞争条件可能会破裂。正如我所说,这段代码从2分钟到30秒内收到了一份报告......而且需要花费几分钟时间才能完成。没有要创建的MySQL模式,也没有要安装的Memcache或Redis。
在一封信中,这是PHP文档(http://php.net/manual/en/class.sessionhandlerinterface.php)上提供的示例实现,并且在读取时不会锁定会话文件。
<?php
class MySessionHandler implements SessionHandlerInterface
{
private $savePath;
public function open($savePath, $sessionName)
{
$this->savePath = $savePath;
if (!is_dir($this->savePath)) {
mkdir($this->savePath, 0777);
}
return true;
}
public function close()
{
return true;
}
public function read($id)
{
return (string)@file_get_contents("$this->savePath/sess_$id");
}
public function write($id, $data)
{
return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
}
public function destroy($id)
{
$file = "$this->savePath/sess_$id";
if (file_exists($file)) {
unlink($file);
}
return true;
}
public function gc($maxlifetime)
{
foreach (glob("$this->savePath/sess_*") as $file) {
if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
unlink($file);
}
}
return true;
}
}
在PHP 5.4+中,使用它就像在开始会话之前设置处理程序一样简单:
$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();
对于较低版本的PHP,您仍然可以通过函数调用来完成...请参阅PHP文档。
答案 2 :(得分:1)
您可以通过在session_write_close()之后调用session_start()来重新启动会话。然而,这将导致多个SIDS。我通过在刷新输出之前从标题中删除多个SIDS来解决此问题。
请参阅此示例:https://gist.github.com/CMCDragonkai/6912726#file-nativesession-php-L254
答案 3 :(得分:0)
除了使用session_write_close()之外?我不知道。
答案 4 :(得分:0)
无法从php会话中禁用锁定。这是一个真正的nighmare锁定用例。唯一的办法摆脱会话和/或PHP。作为临时解决方案,您可能希望使用riak会话处理程序:https://github.com/zacharyfox/riak-php-sessions它是免费锁定的,并且它正在运行。
memcached会话处理程序的最新版本还出于某些疯狂的原因引入了锁定,无法禁用它。
答案 5 :(得分:0)
如果PHP即使在调用session_write_close之后也没有异步处理请求,它可能只是xdebug。我不知道这是不是你的问题,但我一直被这个绊倒并让自己疯狂,所以我想如果其他人有同样的问题我会发布它:)
答案 6 :(得分:0)
这是我在研究会话处理程序时遇到的一个相当古老的问题,但答案是肯定的,但是没有使用默认处理程序(没有深入攻击文件系统来禁用锁定)。
我遇到过同样的问题。需要注意的重要一点是,您必须确切地知道禁用锁定的后果!
我的解决方案是会话处理机制的更大扩展的一部分 - Stackable Session Handler包括与默认处理程序兼容的存储和(可选)非阻塞会话读写。