【问题标题】:PHP script keeps "restarting" creating new instances of itselfPHP脚本不断“重新启动”创建自己的新实例
【发布时间】:2018-12-16 05:38:18
【问题描述】:

我使用 Zend Framework 2 开发了一个网站。它基本上是一个价格比较网站,集成了许多顶级联盟网络。我编写了一个脚本来检查每个附属网络的价格,然后用该价格更新我的本地数据库。根据我联系的附属网络,我可能正在进行 API 调用(Amazon 或 CJ.com),或者我可能正在查看 XML 产品提要(Pepperjam 或 LinkShare)。 XML 产品提要将在本地托管。

目前,我正在使用此脚本检查大约 3,500 个 sku。其中绝大多数 (95%+) 都针对 XML 产品提要。我估计这个脚本可能需要大约 10 分钟才能完成。我正在查看的一些 XML 文件大小约为 8 MB。

我已经在我的本地环境中彻底测试了这个脚本,并竭尽全力确保没有内存泄漏或类似性质的东西会导致性能问题。例如,我确保尽可能使用数据流,以避免将 XML 文件一遍又一遍地放入内存中,等等。可以说,脚本在本地运行没有问题。

此脚本旨在作为 cron 作业运行,但我确实有办法通过临时安全管理界面触发它。在本地,这就是我启动脚本运行的方式,一切都很顺利。

当我将代码部署到共享主机帐户时,我遇到了各种各样的问题。为了排除故障,我将日志记录附加到该脚本的各个阶段,以跟踪它的启动时间、进度以及每个步骤的完成时间等。所有这些都将记录到 MySQL 数据库中。

问题 #1:如果我通过 HTTP 请求临时运行脚本,我发现它将运行几分钟,然后脚本再次启动(所以现在有两个实例显然正在运行)。再等几分钟,第三个将启动,等等......这是一个示例,当我通过 HTTP 请求触发脚本在晚上 10:09 运行时。

Screenshot of process manager

不用说,我不会通过 HTTP 请求运行它,因为它只会让我的网络托管服务提供商遇到麻烦 :)

问题 #2:当脚本在服务器上运行时,通过 cron 作业触发,它无法完成。我已经获取了数据库的生产副本并将其与 XML 文件一起在本地获取,它运行良好。因此,坏数据暴露坏代码应该不是问题。我的观察是——脚本几乎运行了完全相同的时间——在中止、终止或其他任何情况下。最后更新的记录通常在脚本触发后大约 4 分 30 秒左右(如果有记忆的话)加盖时间戳。 SKU列表是不断变化的,所以它结束的记录不同,但每次更新的时间几乎相同。错误日志中没有记录任何内容。我通过 SSH top 命令监控服务器资源,并没有什么异常。正在检查 CPU 使用率,并且使用的内存没有增加。

我有一个通过 Bluehost 共享的主机帐户。我的想法是,这可能是脚本最大执行时间问题。我在脚本本身和通过 php.ini 中延长了最大执行时间。没有区别。

所以我想我正在寻找的是关于下一步去哪里的一些新想法。我应该问我的托管公司什么问题,这样他们才能帮助我弄清楚这一点。至少可以说,它们只是有点帮助。这可能是对我的托管帐户的一些限制吗?触发某种正在杀死脚本的自动监视器?对于这种性质的脚本,哪些类型的 Apache 设置可能有问题? PHP.ini 设置?您可以提供的任何意见绝对会有所帮助。

为什么当通过 HTTP 触发时,它会不断启动新实例?我想我可以不用手动运行它,而只能通过 cron 作业运行它,但这也不起作用。所以....有兴趣听到社区对此的想法。谢谢!

【问题讨论】:

    标签: php linux apache cron-task


    【解决方案1】:

    我没有看过你的脚本,也没有和你的主持人合作过,所以下面的一切都只是一个猜测 - 和一个建议。

    根据您的描述,我会说您是对的,您的脚本在从 cron 运行时可能因超时而被终止。我不确定为什么当您通过 HTTP 请求手动执行脚本时它会不断产生新的脚本实例,但它也可能与超时有关(例如,如果他们有一个逻辑可以在脚本没有产生的情况下重新启动脚本在一定时间内输出,或类似的东西)。

    您可以与您的托管服务提供商联系,了解在他们的环境中运行长时间运行(或消耗内存)的脚本,他们可能已经编写了一些涵盖该主题的常见问题解答或文档。

    如果您的提供者无法提供帮助,请让我为您推荐一个选项。

    根据您的说法,我希望您的脚本运行 SQL 查询以获取 SKU 列表,然后缓慢地迭代此列表,对每个项目执行一些工作(最终无论出于何种原因而死,正如我们所了解的)。

    如果您创建一个临时表(或文件 - 只是服务器上的任何类型的持久存储),它将保存脚本的最后处理记录 ID,或者如果脚本成功完成,则为 NULL。这样您就可以让您的脚本从最后处理的记录开始(如果最后处理的记录的 id = 1000,则将 ... WHERE id > 1000 添加到获取 SKU 的主查询中),并且您不会真正关心是否脚本是否完成了第一次尝试(如果没有,它将在第二次尝试时从被杀死的那一刻开始继续处理)。

    或者,要扩展此方法,您可以将一次调用限制为要处理的特定数量的记录(例如 100 或 1000),再次将最后处理的记录 ID 保存在数据库或其他位置。

    主要思想是:如果脚本无法一次处理所有 SKU,只需使其可重新启动,这样它就不会丢失进度。

    【讨论】:

    • 感谢您的回复。当它通过 cron 作业运行时,在终止后,它不会自行重新启动。我确实考虑过将作业安排为每 30 分钟或类似的时间运行一次,然后按照您的建议订购 SKU 查询。每个 SKU 记录都有一个包含时间戳的“LastUpdated”字段。我有点认为这是最后的选择,但我的完美主义者想找出根源问题。我会联系我的托管服务提供商,看看他们在这方面是否有帮助。
    猜你喜欢
    • 2013-10-12
    • 1970-01-01
    • 1970-01-01
    • 2022-07-13
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 2018-04-25
    • 1970-01-01
    相关资源
    最近更新 更多