php正在锁定一个已删除的会话文件


php is locking a deleted session file

我们的php应用程序在apache下运行,有时会试图锁定已删除的会话文件,这会挂起apache进程,最终我们会耗尽进程。

证据:

strace : flock(89, LOCK_EX                   -----stuck in flock for File Descriptor 89

然后:

lsof,对于相同的过程:

httpd 22161 apache 89u ... /var/lib/php/session/sess_mf7svvirg7rbu9l0m5999pm6h4 (deleted)

关于为什么会发生这种情况,有什么想法吗?我们能做些什么来防止它?

我遇到过一个与您的症状相同的问题,只是会话文件是否被删除无关紧要(可能对您来说也是如此——您有证据表明会话文件的删除与flock有关吗?)。

在我的案例中,这是两个脚本之间访问同一会话文件的竞争条件:

  1. /page1.php在加载时执行良好。它包括对/background.php执行XMLHttpRequest的javascript
  2. 当访问/background.php时,它运行readfile(http://remote.url),这是一个不存在的URL
  3. 当导航到/page2.php时,脚本将被搁置。strace显示flock(89, LOCK_EX,lsof表示它正在等待对会话文件的读/写访问

在这种情况下,/page2.php/background.php都在等待同一个会话文件,但后者无法这样做,因为它在等待readfile()超时。我在php_error_log:中得到了这个

PHP Warning:  readfile(http://remote.url): failed to open stream: Connection timed out in […]/background.php on line […]

因此,问题出在一个与被认为是罪魁祸首的剧本完全不同的剧本上。

您可以通过grepping php会话文件的所有打开的文件来快速检查这个问题,以查看哪个httpd进程正在使用它:

$ sudo lsof | grep sess_it9q6kkvf83gcj72vu05p9fcad7
httpd     31325    apache   74u      REG                8,5       410   11634061 /var/lib/php/session/sess_it9q6kkvf83gcj72vu05pfa543
httpd     31632    apache   74uW     REG                8,5       410   11634061 /var/lib/php/session/sess_it9q6kkvf83gcj72vu05pfa543

如果两个httpd进程正在访问同一个会话文件,那么这可能是您的问题。现在,您可以使用strace检查这些过程在做什么,或者在我的情况下,使用apachectl fullstatus:就足够了

$ sudo apachectl fullstatus | grep 31632
5-7  31632 0/1/1169  _ 0.05 6     30692 0.0  0.02  3.45 111.222.333.444 www.example.com.com     /background.php

是否有任何巨大的(客户端)需要删除会话文件?你不应该那样做。PHP本身实现了一种垃圾收集机制来删除不起作用的会话文件。它将比使用PHP编写的任何其他东西都要高效得多。

您可以使用:

  1. session_destroy--销毁注册到会话的所有数据
  2. session_unset-释放所有会话变量

然后再次执行session_start()。

点击此处查看更多信息。

更新:

PHP是一个非常干净的脚本引擎,它不会在您的系统中留下垃圾!达到会话超时后,会话文件将自动删除。session-timeout可能是你的系统的情况,我想是的。因此,如果超时设置为20分钟,文件将在上次访问后20分钟被删除。饼干也是如此。每次请求页面时,cookie ttl都设置为现在+20分钟。

查看一些配置指令。很可能您已经将gc_maxlifetime设置为一个非常高的值,而它们根本没有达到要自动收集的"过期"限制。您应该检查您的应用程序,以确保它们在运行时没有为会话设置(使用ini_set())设置奇怪的值。

PHP有一个在session_start()上运行的内部算法,它决定是否应该运行垃圾清理并删除旧的会话文件。出于性能原因,我认为每次运行的默认机会是1%,因此平均每100个session_start()调用中就会启动一次垃圾收集例程。如果发现这还不够,可以将gc_disper设置提高到高于1的值。

这可能更适合您的情况,但为什么不将会话从文件系统完全迁移到数据库呢?

你可以在这里获得更多信息http://php.net/manual/en/function.session-set-save-handler.php

你试过读取那两个打开的bug吗?

https://bugs.php.net/bug.php?id=47640https://bugs.php.net/bug.php?id=32092

试着调用session_write_close()(丑陋的解决方法)作为最后一行,或者升级PHP。

不知道您的案例的原因。

这个问题得到了一些很好的建议。。。

您是否尝试重新安装环境,或移动到其他服务器?如果您否决了默认的会话处理程序,并使用fopen($file,'w')而不是fopen($file,'x'),它会起作用吗?

此外,实现基于数据库的会话处理程序的解决方法,例如,有一百万左右的指南来设置"php和mysql会话处理程序"。