PHP 中的随机会话数据丢失问题

17

以下是我们过去几周面临的问题。

1/ 我们的设置

  • PHP 5.4 + MySQL
  • 2个专用服务器,负载平衡
  • 使用memcached将会话在2个服务器之间复制
  • 在这些服务器上运行3个应用程序:
    • 一个自定义开发的应用程序,使用默认的php会话设置
    • 另一个自定义开发的应用程序,使用不同的会话设置(cookie名称、路径)
    • 一个WordPress CMS

2/ 问题

问题出现在我们的第一个应用程序中。

一些用户报告说,他们有时会在几分钟后断开连接(当会话设置为持续3个小时时)。他们可能在同一天多次遇到这种情况,然后在几天内没有任何断开连接的问题,但问题总是会再次出现。到目前为止,受影响的用户比例很小,但我希望在它“传播”到其他用户之前解决这个问题。

问题似乎发生在应用程序的不同位置,尽管我们已经确定了大部分错误发生的三种情况:

  • 一些涉及提交表单($_SESSION变量被修改)
  • 其他涉及打开一个弹出页面,没有修改会话数据

我们尝试复制用户描述的不同场景:有时我们能够做到,但大多数情况下我们没有任何问题,这使得调试变得困难。

其他注意事项:

  • 问题是最近出现的,这个应用程序在没有任何问题的情况下运行了多年。
  • 它似乎与我们的服务器负载无关,因为即使在我们的流量较低的暑假期间也仍然会出现问题。
  • 它只影响一个会话/用户:所有其他同时登录的用户都没有遇到这个问题。
  • 这个问题发生在所有不同的浏览器上(IE、Firefox、Chrome)。

3/ 技术分析

当会话断开时,用户会被重定向到一个页面 "您的会话已过期或您没有查看的权限"。当加载该页面时,我们会收到一个带有 $_SESSION 变量转储的技术电子邮件。

当会话正常过期时,我们收到的电子邮件显示 $_SESSION 变量为空(正常行为)。 当发生意外断开连接时,有趣的是 $_SESSION 并不完全为空:在数组包含的 ~20 个元素中,只剩下一个(总是相同的)。

这意味着会话并未过期,但留下的数据不足以 "识别" 用户,因此显示 "无权访问" 页面。当发生这种情况时,我们可以在 memcached 中检查此会话仍然包含一些数据,以作确认。

目前我们已经确定了以下可能的问题原因,并采取措施加以排除:

  • Memcached 指示空闲空间在 70% 至 80% 之间,因此我们认为它不是问题所在。
  • 我们移除了 Memcached,回到使用 NFS 共享目录来存储会话文件:问题实际上变得更糟了。这可能指向一个应用程序错误,因为 NFS 写入数据速度较慢,会更容易导致会话丢失。
  • 我们浏览了所有不同的论坛(包括 SO)讨论 PHP 会话数据丢失,并根据需要调整了我们的代码。代码库很大,但我们使用了自动化工具和脚本来避免错过任何文件。
    • session_start() 在每个页面开头被调用。
    • header("Location...") 后调用 exit()。
    • register_globals 已关闭。
  • 我们已经测试了我们的另外两个应用程序与有问题的应用程序之间可能发生的互动,尽管它们不共享任何代码、数据库或会话处理。在那里没有发现任何问题。
  • 我们已经分析了断开连接时的访问日志,以检查行为模式:但是没有发现任何有用信息。

因此,我们不知道导致这个问题的原因,因为它似乎是随机发生的。所以我的问题是:

  • 问题可能来自我们的代码:我们是否错过了任何要检查的内容?尽管该代码大部分时间都适用于所有用户,但我仍在考虑这种可能性。
  • 问题可能来自另一个应用程序/进程,该应用程序/进程可能会“清空”一部分会话变量数组。我们也已经审核了其他应用程序的代码,但没有找到可以引起此问题的原因。如果另一个进程正在执行此操作,为什么它只会清空一些会话而不是所有会话?

谢谢您的帮助。


3
问题的引入是否巧合地与 PHP 的升级同时发生? - Niels Keurentjes
我遇到这个的机会很小...我们的Memcached服务器也存在随机会话丢失的问题。这是一个PHP应用,有两台运行PHP 5.3的服务器进行负载平衡。我们还有一个专用的数据库服务器,上面安装了Memcached。一些用户在登录后2分钟后会话过期,而其他用户则可以整天保持登录状态。 - Wayne Whitty
2
你可以尝试检查一下这个会话损坏是发生在两台服务器上还是只有其中一台。 - Tomor
我们在几年前的生产环境中也遇到了类似的问题,原因是负载均衡器没有设置平衡的 cookie。因此,用户停留在一台机器上的点击次数纯属运气。通过向虚拟主机添加必要的指令来解决这个问题。 - Tomor
更多信息:
  • 问题并非在 PHP 升级后出现
  • 我们尝试不使用 memcached,但实际上使用 NFS 后问题变得更严重(我已将此评论添加到我的原始帖子中)
  • 该问题在两个服务器上都出现
- Yann
显示剩余2条评论
3个回答

1
我不认为你会得到一个明确的答案。可能的原因太多了,而且你没有展示任何代码。
不过,我的猜测是你已经关闭了memcached.sess_locking,或者如果你有自定义的会话实现,它根本没有实现锁定。
最终,这会导致两个同时进行的HTTP请求之间的竞争条件。
我的猜测基于常见的错误建议,即关闭锁定或尽快释放它们,以达到更高的性能。

我总是在控制器上完成锁定后立即释放它们,除了处理登录和注销的身份验证控制器之外,我通常没有需要在会话中锁定的内容。其他所有内容都可以安全地知道它正在执行的操作不会更新会话。当然,这要求我显式调用SessionManager :: Dispose()来释放它(在底层调用session_write_close()并确保从那时起任何对__set的调用都记录错误并抛出异常)。 - scragar
你做得很好,@scragar。关闭会话是正确的做法,问题在于当您释放锁定时会话仍处于活动状态。 - Narf
我们检查了:会话锁定已激活。 - Yann

0
如果出现了这个问题,请检查发生了什么变化。您是否对应用程序进行了任何工作?如果是,请检查提交的代码(您谈到了自动化工具,因此我希望有一个存储库,可以准确地找到代码更改)。您是否更改了服务器上的任何内容?例如升级软件、升级/更改硬件、对其他两个应用程序进行更改?有一件事情突然想到,您是否检查了用于缓存的驱动器?它可能是文件系统的某个损坏部分。这可以解释随机用户部分。
我总是做的一些事情是:
  • 尽可能准确地确定第一次出现的时刻。在我的工作中,这有时会引发某人说“哦,是我更改/更新/创建这个或那个时可能有关”,因此这可能会有所帮助。另一方面,有时需要几天、几周甚至更长时间才能注意到某些事情,因此如果没有发现任何问题,请开始扩大时间范围。
  • 您已经有了几种情况,找到它们的共同点。如果它们没有共享任何代码,请停止查找。如果它们共享代码,请在那里搜索。当然,在此共享(部分)代码可能会让我们帮助您进行搜索。
  • 进行有组织的搜索。我通常在我最多工作于应用程序时(或者最好是我创建它时)进行主要应用程序检查。同事将检查可能对其产生影响的周围应用程序。在您的情况下,还有其他两个应用程序。最后,我们的系统管理员将检查服务器上新安装或更新的软件,并与我们的网络专家核实是否有任何硬件或网络相关的更改(对于其他人来说,这可能是托管提供商)。

0

这可能只是一个使用会话并调用session_name()session_id()的WordPress插件,使用不同的值,将您的自定义应用程序与默认会话设置重叠。

由于WordPress本身不使用会话,因此插件通常从具有自由控制会话的角度编写。我在WordPress测试站点上进行了搜索,并发现在图库插件、放置页面背景图像的插件、购物车插件以及我正在编写的需要将上传的文件从一个管理页面传递到另一个管理页面的插件中使用了会话。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接