【问题标题】:How to run a php script in a shell on my website?如何在我的网站上的 shell 中运行 php 脚本?
【发布时间】:2019-01-28 10:15:31
【问题描述】:

我有一个位于/var/www/site/update.php full 的PHP 脚本。脚本从 cron 自动启动:

/usr/bin/php /var/www/site/update.php full

但是当我从我的网站启动相同的脚本时:

<?php exec("/usr/bin/php /var/www/site/update.php full") ?>

它运行了大约 20 分钟,然后开始显示大量错误消息。同时页面停止加载并在屏幕上写入error 504 Gateway Time-out

我猜 curl 也无济于事。还有其他选择吗? 问题是如何运行,以便它可以独立于浏览器工作。该代码已经存在并已解决。现在它每小时运行一次。但是需要通过按网站上的按钮或链接来计划外运行它。

【问题讨论】:

  • 您需要刷新缓冲区以立即获得输出,在这种情况下您将避免网关超时
  • 你在使用 PHP-FPM 吗?
  • 你有调用 save_handler 吗?
  • 你必须在网络服务器上运行这个脚本吗?另一种方法是设置每分钟运行一些 CRON 任务并检查是否有一些工作要做。然后在网页中按下按钮 - 只需为该 cron 任务设置一些标志,如果可以启动所需的工作。此操作将很快不会阻塞页面加载。繁重的任务不应该直接在 Web 服务器中执行
  • 谢谢。但我忘记了我有参数。我更新了我的问题。

标签: php http-status-code-504


【解决方案1】:

您可以在 PHP 脚本中修改 set_time_limit(-1); 以防止 PHP 死机,但 504 听起来像是您正在运行 nginx,因此您还必须使用 location 配置节点(或 main ngix config) 来设置 *_timeout 设置

如果您不需要每秒刷新输出,我会这样做:

  1. 按下按钮时,将“按下”标志存储在某个文件中

    if($_POST['schedule_script_button']){ touch(DIR."/script_is_scheduled.flag"); }

  2. 使用 cron 每分钟运行一次脚本

* * * * * php /var/www/example.com/my-long-script.php

  1. 脚本必须检查是否设置了该标志,否则终止

    if(!file_exists(DIR."/script_is_scheduled.flag")){ 出口; } //这里执行完整的脚本逻辑

  2. 如果设置了标志,则运行脚本并将输出保存到文件中。如果你要使用crontab,你可以在脚本中使用echo,并且crontab应该修改如下:

* * * * * php /var/www/example.com/my-long-script.php &gt; /var/www/example.com/my-script-output.txt

  1. 允许 Web 界面从另一个 URL 读取“输出文件”。即您现在可以使用file_get_contents PHP 函数读取my-script-output.txt 并简单地回显其内容。

我反对将 web-url 的执行限制设置为较高的值,攻击者可以使用它来通过长脚本执行来占用您的网站来关闭您的网站

【讨论】:

  • 如果我可以通过单击按钮运行 cron,它就已经解决了问题。请展示如何使用网站上的按钮运行事件冠?
  • 脚本将每分钟运行一次并检查标志。如果您需要即时运行 - 只需在其中实现 rabbitMQ(它可以比 cron 更快地触发)并在按下按钮后重定向到“显示文件的输出”页面。如果您不想使用最佳解决方案,只需更改 PHP 和 nginx 超时。
  • 有什么方法可以简单地启动 cron 事件吗?
  • 不,您不能从网站“启动”cron,cron 是预定的作业。 “flag”可以是一个空文件,稍后可以使用file_exists 函数检查(设置标志)。如果文件存在 - 启动脚本,如果文件不存在,跳过运行脚本(简单的 exitdie )。
  • 您的答案与服务器上发生的情况接近。但是当您将站点移动到另一台服务器时,我希望这样做不依赖 cron。
【解决方案2】:

您试图实现的目标无法在 PHP Web 范例中完成:您的脚本在 HTTP 请求的上下文中运行;当您的访问者中断请求(关闭选项卡或浏览器,关闭 WiFi 等)时,它的执行将始终终止。

为了从 HTTP 请求(单击按钮或程序化 curl 调用)触发执行长后台任务,您的服务器必须由更多成分组成:通常是消息队列(例如 @ 987654321@).

按钮点击只会触发 Job 消息:将(字符串)消息放入 Message Queue 中,描述要运行的执行类型和参数(例如以 PHP 序列化方式或 JSON) - 和然后立即返回。您的访问者会收到一个快速的 HTTP 响应,上面写着“您的工作已成功排队”。

然后,除了 Message Queue 成分之外,您还必须运行一个持续运行的进程(在具有无限时间限制的 PHP CLI 中,或使用您选择的任何语言的守护程序)。这个无限循环的脚本将自己置于针对消息队列服务的侦听模式(订阅者),每次收到消息时,它都会执行您需要的任何长时间处理。

您的访问者可能会在他们正在运行的作业的专用页面上查询长作业的状态,他们也可能会在其中获得下载或查看等结果(例如:各种“将 YouTube 转换为 Mp3”服务围绕网络)

这是处理在线触发的长后台任务的唯一可靠方法。

您可以使用多种替代中间件来实现此目的:RabbitMQRedisKafka 是一些示例,具体取决于您的具体情况(根据考虑因素,例如daemon 实例,需要对队列中的未决消息进行优先级管理,或者在发生故障时保持队列等)

您可以在 RabbitMQ How-To 中找到 detailed guide with PHP

【讨论】:

  • 谢谢。我如何使用 redis 来完成这项任务?请给链接。 Redis作为缓存安装在服务器上,我可以使用它。
  • @romanown 我认为您应该接受答案,并使用 Redis 标记发布一个新问题,关于如何在 PHP 中使用 Redis 实现发布/订阅模式 :-) 然后将链接粘贴到您的问题,我会看看我是否知道答案。
  • 我在redis的网站上看了,没有发现可以运行脚本。这是一个解决方案吗?
  • @romanown 我指出了一个合适的设计来满足您的要求。您应该将我的答案标记为已接受的答案,并发布另一个关于 Redis+PHP 的问题,以便其他人可以回答(包括我)。我无法在评论中提供进一步的解释和代码示例——举一个 Redis 队列实现的例子超出了你最初提出的问题。谢谢
  • 我正在创建关于 Redis 的新问题。 stackoverflow.com/questions/51974188
【解决方案3】:

我按照本次讨论php execute a background process 中的建议进行了操作。页面仍未加载,但其结果是。这可能不是最好的解决方案,但它确实有效。我期待一个更好的解决方案。

【讨论】:

  • 这是因为,尝试在后台进程中执行 PHP 是不好的做法。有现代、强大、记录在案的方法可以实现您想要做的事情。
  • 谢谢。我理解这一点,稍后会尝试使用 RabbitMQ。但是使用 Redis 有人告诉我,在他的帮助下这是不可能的。
猜你喜欢
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 2012-11-04
  • 2023-04-03
  • 2017-07-15
  • 2016-12-16
  • 1970-01-01
  • 2020-08-07
相关资源
最近更新 更多