以下是我们过去几周面临的问题。
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 已关闭。
- 我们已经测试了我们的另外两个应用程序与有问题的应用程序之间可能发生的互动,尽管它们不共享任何代码、数据库或会话处理。在那里没有发现任何问题。
- 我们已经分析了断开连接时的访问日志,以检查行为模式:但是没有发现任何有用信息。
因此,我们不知道导致这个问题的原因,因为它似乎是随机发生的。所以我的问题是:
- 问题可能来自我们的代码:我们是否错过了任何要检查的内容?尽管该代码大部分时间都适用于所有用户,但我仍在考虑这种可能性。
- 问题可能来自另一个应用程序/进程,该应用程序/进程可能会“清空”一部分会话变量数组。我们也已经审核了其他应用程序的代码,但没有找到可以引起此问题的原因。如果另一个进程正在执行此操作,为什么它只会清空一些会话而不是所有会话?
谢谢您的帮助。