【问题标题】:Running a PHP app on windows - daemon or cron?在 Windows 上运行 PHP 应用程序 - 守护进程还是 cron?
【发布时间】:2011-08-11 03:09:42
【问题描述】:

我需要一些实施建议。我有一个 MYSQL 数据库,它将被远程写入,以便在本地处理任务,我需要用 PHP 编写的应用程序在这些任务进入时立即执行这些任务。

当然,我的 PHP 应用程序需要被告知何时运行。我考虑过使用 cron 作业,但我的应用程序在 Windows 机器上。其次,我需要每隔几秒钟不断检查一次,而 cron 只能每分钟检查一次。

我曾想过编写一个 PHP 守护程序,但我对它的工作原理以及它是否是个好主意感到困惑!

如果有任何关于最佳方式的建议,我将不胜感激。

【问题讨论】:

  • 您可以使用 PHP CLI 将脚本作为计划任务运行,这实际上与 cron 作业相同。但是,我认为定时任务的最小时间间隔是 5 分钟,所以当任务进来时它不会立即运行。
  • @Brian - 我不知道。但我需要每 5 秒左右一次。
  • @Kay - 如果是这种情况,那么我认为您要么必须让脚本可以按需运行(例如,将其托管在 IIS 中),要么创建一个守护进程。
  • 如果导致此更新成为必要的事件是 Mysql 内部另一个操作的结果,那么这可能是使用 mysql 触发器的情况吗?
  • @Cups - 我认为使用触发器意味着不再有排队系统,因为每个触发器只会处理该任务。我希望处理前 20 个任务,以免服务器超载。完成后,我会做接下来的 20 个。

标签: php windows daemon


【解决方案1】:

这看起来像是一个工作服务器的工作;)看看Gearman。这种方法的另一个好处是,这是由远程端触发的,当且仅当有事情要做时,而不是轮询。尤其是在小于(比方说)5 分钟的时间间隔内,轮询不再非常有效,具体取决于作业执行的任务。

【讨论】:

  • 如果有多个源正在写入作业数据库,这会起作用吗?我的意思是他们都必须使用 gearman 框架?这似乎是完成这项任务的一个大工具!
【解决方案2】:

快速而肮脏的方法是创建一个循环来不断检查是否有新工作。

伪代码

set_ini("max_execution_time", "3600000000"); 
$keeplooping = true;
while($keeplooping){

   if(check_for_work()){
      process_work();
   }
   else{
     sleep(5);
   }

   // some way to change $keeplooping to false
   // you don't want to just kill the process, because it might still be doing something
}

【讨论】:

  • 如果服务器重新启动,或者 apache 重新启动,这个脚本将不得不重新启动,老实说这并不完全是防弹的。
  • 绝对正确。我不知道 PHP 可以在 Windows 上为自动执行做什么,除了可能使用计划任务或“启动”项启动它。也许您想研究创建一个可以提取工作的 Windows 服务,然后将其传递给 PHP 进行处理?
【解决方案3】:

我会提出一些不同寻常的建议:您说您需要在将数据写入 MySQL 时运行任务。这意味着 MySQL“知道”应该执行某些事情。 这听起来像是 MySQL 的 UDF sys_exec 的完美场景。

基本上,如果 MySQL 可以在发生某些事情时调用外部程序,那就太好了。 如果您使用提到的 UDF,您可以从内部执行一个 php 脚本——比如说,INSERT 或 UPDATE 触发器。 另一方面,您可以使其更加资源友好并创建 MySQL 事件(假设您使用的是适当的版本),该事件将使用 sys_exec 调用 PHP 脚本,该脚本以预定义的时间间隔执行某些更新 - 这减少了对 Cron 或任何可以按预定时间间隔执行某事的类似程序。

【讨论】:

    【解决方案4】:

    pyCron 是 Windows 的一个不错的 CRON 替代方案:

    由于这个任务非常简单,我只需将 pyCron 设置为每分钟运行以下脚本:

    set_time_limit(60); // one minute, same as CRON ;)
    ignore_user_abort(false); // you might wanna set this to true
    
    while (true)
    {
        $jobs = getPendingJobs();
    
        if ((is_array($jobs) === true) && (count($jobs) > 0))
        {
            foreach ($jobs as $job)
            {
                if (executeJob($job) === true)
                {
                    markCompleted($job);
                }
            }
        }
    
        sleep(1); // avoid eating unnecessary CPU cycles
    }
    

    这样,如果计算机出现故障,您将有 60 秒的最坏情况延迟。

    您可能还想研究信号量或某种锁定策略,例如使用 APC 变量或检查是否存在锁定文件以避免竞争条件,例如使用 APC:

    set_time_limit(60); // one minute, same as CRON ;)
    ignore_user_abort(false); // you might wanna set this to true
    
    if (apc_exists('lock') === false) // not locked
    {
        apc_add('lock', true, 60); // lock with a ttl of 60 secs, same as set_time_limit
    
        while (true)
        {
            $jobs = getPendingJobs();
    
            if ((is_array($jobs) === true) && (count($jobs) > 0))
            {
                foreach ($jobs as $job)
                {
                    if (executeJob($job) === true)
                    {
                        markCompleted($job);
                    }
                }
            }
    
            sleep(1); // avoid eating unnecessary CPU cycles
        }
    }
    

    如果您坚持使用 PHP 守护程序,请帮自己一个忙,放弃这个想法,改用 Gearman。

    编辑:我曾经问过一个您可能感兴趣的相关问题:Anatomy of a Distributed System in PHP

    【讨论】:

      【解决方案5】:

      我绝对不建议为此使用 cronjobs。

      cronjobs 是一件好事,非常有用且易于用于许多目的,但是当您描述您的需求时,我认为它们可能会产生更多的复杂性而不是它们的好处。以下是一些需要考虑的事项:

      • 如果工作重叠会发生什么?一个需要比一分钟更长的时间来执行?是否有任何共享资源/死锁/临时文件? - 最常用的方法是使用一个锁文件,如果它在程序开始时被占用就停止执行。但该计划还必须在完成之前寻找更多工作。 - 但是,这在 Windows 机器上也会变得复杂,因为它们 AFAIK 不支持开箱即用的写锁

      • cronjobs 很难维护。如果要监视它们,则必须实现其他逻辑,例如检查程序上次运行的时间。但是,如果您的程序只应按需运行,这可能会变得很困难。最好的方法是在数据库中添加某种“作业已完成”字段或删除已处理的行。

      • 在大多数基于 unix 的系统上,cronjobs 现在相当稳定,但是有很多情况可以破坏你的 cronjob 系统。其中大多数是基于人为错误。例如,系统管理员未在编辑模式下正确退出 crontab 编辑器可能会导致所有 cronjobs 被删除。由于上述原因,许多公司也没有适当的监控系统,并在他们的服务遇到问题时立即通知。在这一点上,通常没有人写下/将哪些 cronjobs 应该运行并置于版本控制之下,开始疯狂的猜测和重建工作。

      • 当使用外部工具并且环境不是本机 unix 系统时,cronjob 维护可能会更加复杂。系统管理员必须了解更多程序,他们可能会遇到潜在错误。

      老实说,我认为从控制台启动并打开一个小脚本就可以了。

      <?php
      while(true) {
       $job = fetch_from_db();
       if(!$job) { 
          sleep(10) 
       } else {
          $job->process();
       }
      }
      

      您还可以在每个循环中触摸一个文件(修改修改时间戳),并且您可以编写一个 nagios 脚本来检查该时间戳是否过期,以便您知道您的工作仍在运行...

      如果你想让它与系统一起启动,我推荐一个守护进程。

      ps:在我工作的公司里,我们的网站有很多的后台活动(爬网、更新流程、计算等),当我开始工作时,cronjobs 真是一团糟.它们分布在负责不同任务的不同服务器上。数据库在互联网上被广泛访问。大量的 nfs 文件系统、samba 共享等用于共享资源。这个地方充满了单点故障、瓶颈和不断破裂的东西。涉及的技术太多,维护起来非常困难,当某些东西无法正常工作时,它需要数小时的时间来追踪问题,甚至还要花一个小时的时间来解决该部分应该做的事情。

      现在我们有了一个统一的更新程序,它负责几乎所有的事情,它在多台服务器上运行,并且它们有一个配置文件来定义要运行的作业。每一件事都是从一个执行无限循环的父进程中分派的。它易于监控、定制、同步,一切运行顺利。它是多余的,它是同步的并且粒度很好。所以它并行运行,我们可以扩展到任意数量的服务器。

      我真的建议坐下来充分考虑所有事情,并全面了解整个系统。然后投入时间和精力来实施一个解决方案,该解决方案将在未来很好地发挥作用,并且不会在您的系统中传播大量不同的程序。

      pps:

      我读了很多关于 cronjobs/tasks 最小间隔 1/5 分钟的文章。您可以使用接管该间隔的任意脚本轻松解决此问题:

      // run every 5 minutes = 300 secs
      // desired interval: 30 secs
      $runs = 300/30; // be aware that the parent interval needs to be a multiple of the desired interval
      for($i=0;$i<$runs;$i++) {
       $start = time();
       system('myscript.php');
       sleep(300/10-time()+$start); // compensate the time that the script needed to run. be aware that you have to implement some logic to deal with cases where the script takes longer to run than your interavl - technique and problem described above
      }
      

      【讨论】:

      • 他需要“每隔几秒检查一次”。为什么你说 cronjob 不是解决这个问题的正确工具?计划任务不是专门用来做这些事情的吗?您的建议似乎比简单的 cron 更复杂。
      【解决方案6】:

      你试过windows scheduler(Windows默认自带)吗?在此您需要提供 php 路径和您的 php 文件路径。效果很好

      【讨论】:

      • 它不会做任何少于每日计划的事情,即没有每小时或每分钟等。我使用的是 Windows XP 机器。
      • 好吧.. Alix Axel 从下面尝试 Pycron 怎么样
      【解决方案7】:

      你不能只写一个java/c++ 程序在设定的时间间隔内为你查询吗?您可以将其包含在启动程序列表中,以便它始终运行。一旦找到任务,它甚至可以在单独的线程上处理它,并处理更多请求并将其他请求标记为完成。

      【讨论】:

        【解决方案8】:

        最简单的方法是使用embed Windows schedule。

        使用 php-cli.exe 运行您的脚本,并使用您的脚本需要的扩展名填充 php.ini。

        但我应该说,实际上您不需要这么短的时间间隔来运行您的计划作业。只需进行一些测试即可为您的测试获得最佳时间间隔值。不建议设置小于 1 分钟的时间间隔。

        还有一个小建议:在脚本开头创建一些锁定文件(php flock 函数),检查是否可以写入该锁定文件,以防止两个或多个副本在脚本末尾同时工作解锁后取消链接。

        如果您必须将输出结果写入数据库,请尝试使用 MySQL TRIGGERS 而不是 PHP。或者在 MySQL 中使用事件。

        【讨论】:

          猜你喜欢
          • 2016-01-29
          • 2023-03-02
          • 2022-08-22
          • 1970-01-01
          • 2016-03-31
          • 2023-03-10
          • 2017-11-25
          • 2018-11-08
          • 1970-01-01
          相关资源
          最近更新 更多