负载平衡集群中的 PHP 会话 - 怎么样?

Posted

技术标签:

【中文标题】负载平衡集群中的 PHP 会话 - 怎么样?【英文标题】:PHP sessions in a load balancing cluster - how? 【发布时间】:2010-11-02 22:56:16 【问题描述】:

好的,所以我得到了这个完全罕见的负载平衡 php 网站的独特场景。令人遗憾的是 - 它以前不是负载平衡的。现在我们开始遇到问题了...

目前唯一的问题是 PHP 会话。自然一开始没有人想到这个问题,所以 PHP 会话配置保持默认。因此,两台服务器都有自己的少量会话文件存储,不幸的是用户将下一个请求抛出到另一台服务器,因为这没有他在第一个服务器上创建的会话。

现在,我一直在阅读有关如何解决这种情况的 PHP 手册。在那里我发现了session_set_save_handler() 的好功能。 (而且,巧合的是,this topic 在 SO)整洁。除了我必须在网站的所有页面中调用这个函数。未来页面的开发人员也必须记住一直调用它。感觉有点笨拙,更不用说可能违反了十几个最佳编码实践。如果我可以翻转一些全局配置选项并 Voilà 会更好 - 所有会话都神奇地存储在数据库或内存缓存或其他东西中。

关于如何做到这一点的任何想法?


补充:澄清 - 我希望这是标准解决方案的标准情况。仅供参考 - 我有一个可用的 mysql 数据库。肯定有一些现成的代码可以解决这个问题吗?当然,我可以编写自己的会话保存内容,Greg 指出的auto_prepend 选项似乎很有希望——但这就像重新发明***一样。 :P
补充2:负载均衡是基于DNS的。我不确定这是如何工作的,但我想它应该类似于this。
已添加 3: 好的,我看到一种解决方案是使用 auto_prepend 选项在每个脚本中插入对 session_set_save_handler() 的调用并编写我自己的数据库持久化器,也许会调用 memcached以获得更好的性能。很公平。

还有什么方法可以避免我自己编写所有这些代码吗?喜欢一些著名且经过良好测试的 PHP 插件?

添加了很多,很久以后:这就是我最后的方式:How to properly implement a custom session persister in PHP + MySQL?

另外,我只是在所有页面中手动包含会话处理程序。

【问题讨论】:

【参考方案1】:

最简单的做法是将负载均衡器配置为始终将同一会话发送到同一台服务器。

如果您仍想使用session_set_save_handler,那么不妨看看 auto_prepend。

【讨论】:

负载平衡是通过 DNS 条目完成的。主机名解析为两个不同的 IP 地址,浏览器随机选择一个。或类似的东西 - 我真的不知道细节。但是AFAIK没有负载均衡器。 “auto_prepend”看起来很有希望。 +1 使用 auto_prepend 在每个脚本之前插入 session_set_save_handler 函数 听起来你已经有了一个“轮询 DNS”——en.wikipedia.org/wiki/Round_robin_DNS。我会说,这个负载平衡的“解决方案”越少越好。 也许吧,但我没选这个也改不了。【参考方案2】:

当我们遇到这种情况时,我们实现了一些位于公共标头中的代码。

基本上对于每个页面,我们都会检查我们是否知道会话 ID。如果我们不检查我们是否处于您描述的情况,通过检查我们是否在 DB 中存储了会话数据。否则我们只是开始一个新会话。

显然,这需要将所有相关数据复制到数据库中,但如果您将会话数据封装在单独的类中,则可以正常工作。

【讨论】:

【参考方案3】:

您没有提到您使用什么技术进行负载平衡(软件、硬件等);但无论如何,解决问题的方法是在负载均衡器上使用“粘性会话”。

总而言之,这意味着当来自“新”访问者的第一个请求进入时,他们会被分配到集群中的特定服务器:所有未来在其会话生命周期内的请求都会被定向到该服务器。在实践中,这意味着为在单个服务器上工作而编写的应用程序可以升级到一个平衡的环境,而代码更改为零/很少。

如果您使用的是硬件平衡器,例如 Radware 设备,则粘性会话将作为集群设置的一部分进行配置。硬件设备通常会给你更细粒度的控制:比如新用户被分配到哪台服务器(他们可以检查健康状态等并选择最健康/利用率最低的服务器),以及更多地控制服务器时发生的事情失败并退出集群。硬件平衡器的缺点是成本 - 但恕我直言,它们是值得的。

至于软件平衡器,这取决于您使用的是什么。对于 Apache,mod_proxy 上有 stickysession 属性 - 并且通过 google 有大量文章可以使用 php session (for example)


编辑: 从原始问题后发布的其他 cmets 来看,听起来您的“平衡”是通过循环 DNS 完成的,因此上述内容可能不适用。我将避免进一步评论并开始对循环 dns 发起攻击。

【讨论】:

我没有选择这个解决方案,也无法更改。【参考方案4】:

您也可以尝试使用 memcache 作为会话处理程序

【讨论】:

【参考方案5】:

您可以设置 PHP 来处理数据库中的会话,因此您的所有服务器共享相同的会话信息,因为所有服务器都使用相同的数据库。

一个很好的教程可以是found here。

【讨论】:

这不是正确的方法。问题是 PHP 会话,忘记数据库,我们正在谈论 PHP。此解决方案只是一种变通方法。 我不同意。将 PHP 会话存储在数据库中是解决此问题的完美解决方案。 @MattFletcher 是一个糟糕的解决方案。应使用 in memory 存储,如 redis、memcached 或 ramfs 等 @r3wt 在内存中?如果由于某种原因必须重新启动会话存储(memcache 等),您是否愿意接受所有会话都将消失? 你可以一直使用 Redis【参考方案6】:

如果您使用 php 会话,您可以与 NFS 共享 /tmp 目录,我认为会话存储在集群中的所有服务器之间。这样你就不需要数据库了。

已编辑:您还可以使用 memcachedb 之类的外部服务(持久且快速)并将会话信息存储在 memcachedb 索引中,并使用内容的哈希甚至会话 ID 来识别它。

【讨论】:

这是当前的“快速解决方法”,但管理员不喜欢这样。关于安全和东西的东西,我不知道细节。 嗯,那么您可以使用像 memcachedb 这样的外部服务,使用键的哈希作为会话的索引。 memcachedb 服务非常快(尤其是编写)且易于使用,您可以使用 PHP 中一组特殊且精简的 memcached 函数。 NFS 不是为大量请求而设计的...请记住,PHP 在session_start() 加载会话并在脚本执行结束时保存它(即使没有更改会话数据!),所以每个 PHP 请求将产生两个 NFS 请求。 我不以这种方式管理会话,但对于无法访问其他(更好的选择)键值存储引擎的人来说,这可能是一种选择。但是,是的,这不是一个很好的选择... :) FWIW,我们浪费了一周的时间试图弄清楚为什么我们的 AWS 托管的 Magento 设置具有两个后端和 NFS 共享缓存目录的速度非常慢。它是 NFS。永远不要这样做!【参考方案7】:

我们处理这个问题的方式是通过 memcached。只需更改 php.ini,如下所示:

session.save_handler = memcache
session.save_path = "tcp://path.to.memcached.server:11211"

我们使用 AWS ElastiCache,因此服务器路径是一个域,但我确信它对于本地 memcached 也是类似的。

此方法不需要任何应用程序代码更改。

【讨论】:

memcached 对于存储会话来说是喜忧参半。它快速、易于设置,但是当内存不足时,它会破坏旧会话,因此永久会话存储将不起作用。在使用它之前,应该真正考虑一下这种数据丢失是否适合他的应用程序。 更多信息在这里:php.net/manual/en/memcached.sessions.php 注意:大部分文档实际上都在那里的 cmets 中!【参考方案8】:

如果您有时间并且仍想查看更多解决方案,请查看 http://redis4you.com/articles.php?id=01..

使用 redis 你是容错的。在我看来,由于这种健壮性,它可能比 memcache 解决方案更好。

【讨论】:

【参考方案9】:

可能为时已晚,但请查看:http://www.pureftpd.org/project/sharedance

Sharedance 是一种高性能服务器,用于集中临时密钥/数据 远程主机上的对,没有 SQL 的开销和复杂性 数据库。

它主要设计用于在网络池之间共享缓存和会话 服务器。通过一个简单的 PHP API 访问共享服务器是微不足道的,并且 它与 PHP 4 和 PHP 5 会话处理程序的预期兼容。

【讨论】:

【参考方案10】:

当涉及负载平衡集群中的 php 会话处理时,最好有粘性会话。为此,请询问维护负载均衡器的数据中心网络以启用粘性会话。一旦启用,您将无需担心 php 结束时的会话

【讨论】:

好吧,如果没有粘性会话,您也可以获得故障转移。 :) 但是,原来的问题有一个不同的问题 - 负载平衡是“DNS 循环”类型。无论如何,粘性会话是不可能的。 :P @Vilx- 我们在使用负载均衡器的 php 会话中遇到了同样的问题,这对我们有用。我们所做的唯一一件事是要求我们的数据中心为配置的域启用粘性会话并仅以循环方式请求路由

以上是关于负载平衡集群中的 PHP 会话 - 怎么样?的主要内容,如果未能解决你的问题,请参考以下文章

负载均衡集群中的session解决方案

负载均衡集群中的session解决方案

生产环境中的Tomcat集群/负载均衡性能

Yii 会话的负载均衡

Kubernetes 集群上的粘性会话

为 Apache/Tomcat 负载平衡启用粘性会话模式