如何在长时间运行的脚本中“触摸”一个 php 会话
Posted
技术标签:
【中文标题】如何在长时间运行的脚本中“触摸”一个 php 会话【英文标题】:How can I "touch" a php session during long running script 【发布时间】:2014-03-26 14:27:12 【问题描述】:我们的 php 项目有一个维护脚本,它以特定的时间间隔运行,并根据个人的时间表执行一系列任务,将其视为 php 的CRON
。
我遇到的问题是,某些以特定间隔出现的任务的运行时间比我们强化的会话长度(30 分钟)长,因此会话在该任务过程中到期,结束了事情过早的。
仅供参考:我不是在谈论脚本运行时间限制,我们已经使用 set_time_limit()
来允许长时间运行的进程。
问题:有没有办法在脚本执行期间以重置会话到期时间的方式“触摸”会话?即 setting 是否适合会话变量,或者是否需要特定的 PHP 函数来完成此操作?
更具体地说,我们有一个实际的 CRON
作业,每分钟运行一次:
* * * * * user curl -XGET https://domain.com/maintenance.php
...它反过来使用我们的内部系统来查找计划在当时运行的任务,其中一些任务短而简单,有些任务长而复杂。正是在长时间复杂(> 30 分钟运行时间)的过程中发生了上述问题。
编辑:总结下面的许多cmets......
我们希望避免发出额外的 RPC 来实现这一点,我正在寻找一个像想象中的 touch_session()
这样的解决方案。
我相信我们可以排除更改会话值的选项,因为在请求关闭之前,更改不会记录在会话中,在我们的例子中为时已晚。
我们正在使用数据库进行会话管理,因此我们可以简单地编写自己的 touch_session()
方法,该方法可以被调用并简单地使用 SQL 查询来更新时间戳,例如 UPDATE sessions SET expiration = '###' WHERE Session_ID = 'abc123';
。 这有什么陷阱吗?
如果不是以上任何一种,还有其他不需要更多 rpc 的选项吗?
【问题讨论】:
那么,您是在使用 cron 作业还是其他方式运行脚本?你能解释一下这与会话有什么关系吗?你能解释一下问题到底是什么吗? 会话超时不会终止脚本执行; max_execution_time 是唯一的限制。 @WesleyMurch 在上面添加了有关维护运行方式的详细信息。 @Gumbo 我们的维护脚本有时会使用CURL
自己调用 API,这需要会话保持完整,这些可能会在父脚本执行期间稍后发生,因此到时会过期他们来了。会话包含权限检查所需的信息。
@Wesley 我不打算详细说明,这与这个问题无关,让我们假设它是必要的。
【参考方案1】:
因此,尽管提出了几种解决方案,但没有一个满足我们的要求,它们要么开销超过其价值,要么存在安全问题,要么需要大量工作才能在大型应用程序中实施。
在 PHP 的情况下,$_SESSION 只是存储在会话记录中的数据的副本,直到请求结束并且新数据覆盖旧数据,因此对 $_SESSION 的任何操作都不会影响实际会话记录在系统。
因为在我们的独特案例中,我们使用 mysql 进行会话管理,所以最简单的答案是使用简单的查询创建我们自己的 touch_session()
方法:
UPDATE sessions SET expiration = NOW() WHERE Session_ID = 'ABC123'
注意,我们的垃圾收集使用 NOW() - 30min 来使记录过期,因此在上面的查询中将过期设置为 NOW()。
这种快速方法现在可以添加到任何潜在的长时间运行的维护脚本中,在这些点上它会被频繁调用以避免会话过期,但不会经常增加任何明显的开销。
希望这可以帮助遇到类似情况的其他人。
【讨论】:
【参考方案2】:正如 cmets 中所说,您可以使用 curl 访问另一个 触及会话的脚本
touch.php
<?php
session_name('theNameOfYourSessionId');
session_start();
在您的维护脚本中使用 curl 调用它,如下所示:
$sid = session_id();
exec('user curl -XGET https://domain.com/maintenance.php?theNameOfYourSessionId='.$sid)
这应该可以解决问题。
您也只需使用file_get_contents 而不是 curl,以避免出现 curl 问题。
$sid = session_id();
file_get_contents('https://domain.com/maintenance.php?theNameOfYourSessionId='.$sid);
另一种方法是简单地接受会话可能已死并从数据库中恢复它们。正如您所提到的,您使用多个应用服务器,并且无论如何您都需要一个多应用服务器解决方案,
那么为什么不使用提供的会话 ID 作为数据库中的标识符并将会话内容存储在那里呢?当您输入脚本并且所需会话为空时,调用数据库并恢复它。
【讨论】:
这是最坏的解决方案中最好的,因为有两个因素,进行额外的 CURL 调用的开销,以及由于任何原因导致脚本一起停止的 CURL 错误的可能性,除非我处理错误处理更麻烦。我希望得到一个快速的回答,比如“当然,使用touch_session();
”或$_SESSION['var'] = microtime();
@oucil 据我所知并就 php.net 的研究而言,不存在诸如 touch_session 之类的东西。对不起。我做了一个编辑添加 file_get_contents 作为选项。也许这对你更好。
感谢您的努力,它们都是有效的,并且可以根据我提出的问题工作。虽然,仅供参考,file_get_contents()
本质上是基于套接字的,并且与curl
的工作方式相似,因此容易受到相同的开销和潜在的通信错误的影响。不过,它们是选项,所以谢谢你。
@oucil 好吧,既然没有 touch_php_session 并且我的回答是有效的,请考虑接受它并将这个问题标记为已回答。
感谢您的输入 Andresch,但考虑到问题中的更新信息(我在这里也提到过)时,这些解决方案存在重大缺陷、开销、需要进行大量错误处理等。当我准备好选择答案时,我肯定会为此 +1,但我会让它打开一段时间,看看是否还有其他想法。在此期间,我继续创建了自己的静态 touch_session()
方法,该方法手动更新会话表中的时间戳,这是使用数据库管理会话的好处。我不认为我会看得更好。以上是关于如何在长时间运行的脚本中“触摸”一个 php 会话的主要内容,如果未能解决你的问题,请参考以下文章