管理长时间运行的PHP脚本的最佳方法?

kba*_*man 75 php apache curl httprequest

我有一个PHP脚本需要很长时间(5-30分钟)才能完成.为了防止重要,脚本使用curl从另一台服务器中抓取数据.这就是它花了这么长时间的原因; 它必须等待每个页面加载,然后再处理它并移动到下一页.

我希望能够启动脚本并让它完成,直到它完成,这将在数据库表中设置一个标志.

我需要知道的是如何在脚本运行完成之前结束http请求.另外,php脚本是最好的方法吗?

sym*_*ean 106

当然可以用PHP完成,但是你不应该将它作为后台任务 - 新进程必须从启动它的进程组中分离出来.

由于人们对此常见问题解答给出了同样错误的答案,我在这里写了一个更全面的答案:

http://symcbean.blogspot.com/2010/02/php-and-long-running-processes.html

来自评论:

简短的版本shell_exec('echo /usr/bin/php -q longThing.php | at now');只是在这里包含有点长的原因.

  • 简短的版本是`shell_exec('echo/usr/bin/php -q longThing.php | at now');`但是这里包含的原因有点长. (5认同)
  • +1该博客文章中坚如磐石的详细答案. (4认同)
  • 是否有机会将相关细节复制到答案中?有太多旧答案链接到死博客.那博客还没有死(但是),但有一天会. (2认同)
  • 我可以知道这个 -q 选项是做什么的吗? (2认同)
  • 链接文章中的更多讨论 - 用户需要包含在 /etc/at.allow 中,selinux/apparmor 需要具体考虑,如果 PHP 运行 chroot,则不起作用,权限必须允许运行 at 和目标进程,实现的语法取决于正确设置的路径。 (2认同)

Flo*_*anH 11

快速而肮脏的方式是ignore_user_abort在php中使用该功能.这基本上说:不关心用户做什么,运行此脚本直到完成.如果它是一个面向公众的站点,这有点危险(因为有可能,如果它启动了20次,你最终会同时运行20个++版本的脚本).

"干净"方式(至少是恕我直言)是设置一个标志(例如在数据库中),当你想要启动进程并每小时(或左右)运行一个cronjob来检查是否设置了该标志.如果设置了,则长时间运行的脚本会启动,如果未设置,则不会发生.


Leo*_*ans 8

您可以使用execsystem来启动后台作业,然后执行该操作.

此外,还有更好的方法来抓取您正在使用的网络.您可以使用线程方法(多个线程一次执行一个页面),或者使用eventloop(一个线程一次执行多个页面).我使用Perl的个人方法是使用AnyEvent :: HTTP.

ETA:symcbean解释了如何在这里正确分离后台进程.

  • 几乎是正确的.只需使用exec或系统就会回来咬你的屁股.请参阅我的回复了解详情. (5认同)

jam*_*ieb 5

不,PHP不是最好的解决方案.

我不确定Ruby或Perl,但是使用Python,您可以将页面刮板重写为多线程,并且它可能至少运行20倍.编写多线程应用程序可能有点挑战,但我编写的第一个Python应用程序是多线程页面刮刀.您可以通过使用其中一个shell执行函数从PHP页面中调用Python脚本.


alj*_*o f 5

是的,您可以在PHP中完成。但是除了PHP,使用队列管理器是明智的。这是策略:

  1. 将大型任务分解为较小的任务。在您的情况下,每个任务可能只加载一个页面。

  2. 将每个小任务发送到队列。

  3. 在某个地方运行您的队列工作者。

使用此策略具有以下优点:

  1. 对于长时间运行的任务,如果在运行过​​程中出现致命问题,它具有恢复的能力-无需从头开始。

  2. 如果不必按顺序运行任务,则可以运行多个工作程序来同时运行任务。

您有多种选择(仅几个):

  1. RabbitMQ(https://www.rabbitmq.com/tutorials/tutorial-one-php.html
  2. ZeroMQ(http://zeromq.org/bindings:php
  3. 如果您使用的是Laravel框架,则队列是内置的(https://laravel.com/docs/5.4/queues),带有适用于AWS SES,Redis和Beanstalkd的驱动程序