【问题标题】:php is locking a deleted session filephp 正在锁定已删除的会话文件
【发布时间】:2012-02-20 16:57:08
【问题描述】:

我们在 apache 下运行的 php 应用程序偶尔会尝试锁定已删除的会话文件,这会挂起 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)

关于为什么会发生这种情况的任何想法?我们可以做些什么来预防它?

【问题讨论】:

  • a) 这个问题可能更适合webmasters.stackexchange.com b) 谁删除了会话文件?你还是 PHP?多久前被删了?您确定它已被删除,而不仅仅是被另一个进程锁定了吗?
  • 你是使用php的会话管理还是自定义会话处理程序?
  • 错误日志中有什么可能与为什么会发生这种情况有关吗?
  • php 的会话处理程序,错误日志中没有任何内容。文件被删除。网站管理员似乎主要关注谷歌分析。该文件不是手动删除的,但我可以查找流氓 cron。
  • 我不知道为什么会发生这种情况,但标准的 Debian 防止方法已经很久了,不让 Apache/a 请求进行垃圾收集,而是设置机会 0 而是运行一个只检查文件时间戳的 cronscript。为了获得更多控制,您可以为这些会话文件定义一个自定义目录,以便可以再次设置超时并且不受其他正在运行的应用程序的阻碍。猜测原因:锁定文件的进程/请求是否也是试图重新打开文件的进程/请求,换句话说:具有相同会话 ID 的请求是否经常清理旧文件?

标签: php


【解决方案1】:

我遇到了与您的症状相同的问题,除了会话文件是否被删除无关紧要(并且可能对您来说也是如此 - 您是否有证据表明会话文件的删除与 @987654321 相关@?)。

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

  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 session 文件的所有打开文件来快速检查此问题,以查看哪个 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

【讨论】:

    【解决方案2】:

    是否有任何巨大的(客户端)需要删除会话文件? 你不应该那样做。 PHP 本身实现了一种垃圾回收机制来删除失效的会话文件。它将是much more efficient,而不是您可以使用 PHP 自己编写的任何其他内容。

    你可以使用:

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

    然后再次执行 session_start()。

    查看更多here

    更新:

    PHP 是一个非常干净的脚本引擎,它不会在您的系统上留下任何垃圾! 达到会话超时后,会话文件将自动删除。 session-timeout 可能与您的系统有关,我认为是这样。因此,如果超时设置为 20 分钟,则文件将在最后一次访问后 20 分钟被删除。饼干也一样。每次请求一个页面时,cookie-ttl 设置为 now + 20 分钟。

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

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

    【讨论】:

    • 我不认为他是故意这样做的......问题是关于为什么 PHP 试图删除会话文件。
    • 我读过is occaisionally trying to lock a deleted session file。所以我给出了这个答案。然后主要关注的是哪个代码正在尝试删除会话文件。
    • 如果 OP 已经知道是什么试图锁定已删除的会话文件,那么就没有问题了。
    • 我从问题中误解了。我会做更多的研究。
    【解决方案3】:

    对于您的情况,这可能更像是一种解决方法,但是您为什么不将会话完全从文件系统迁移到数据库呢?

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

    【讨论】:

      【解决方案4】:

      您是否尝试阅读这 2 个未解决的错误?

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

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

      【讨论】:

        【解决方案5】:

        不知道你的案子的原因。

        这个question得到了一些好的建议...

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

        还有基于数据库实现会话处理程序的解决方法,例如设置“php 和 mysql 会话处理程序”的指南大约有一百万。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-10-24
          • 2017-08-01
          • 2012-05-31
          • 1970-01-01
          • 2016-06-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多