【问题标题】:CakePHP: Run shell job from controllerCakePHP:从控制器运行 shell 作业
【发布时间】:2011-07-14 11:07:52
【问题描述】:

是否可以从 Controller 中使用 dispatchShell?

我的任务是在用户注册后启动一个 shell 作业。

我正在使用 CakePHP 2.0

【问题讨论】:

标签: shell cakephp


【解决方案1】:

如果您无法按照教条建议的方式减少执行此操作的需要,请继续阅读。

因此,您有一个(可能)长时间运行的工作想要执行,并且您不希望用户等待。

由于您的用户正在执行的 PHP 代码发生在 Apache 启动的请求期间,因此执行的任何代码都会停止该请求,直到它完成(除非您遇到 Apache 的请求超时)。

如果您的应用程序不能接受上述内容,那么您将需要通过 Apache 请求(即从命令行)触发 PHP。

在可用性方面,此时通知您的用户您正在后台处理数据是有意义的。从告诉他们稍后可以查看的消息到通过 ajax 轮询您的应用程序以检测作业完成的旋转进度条。

最简单的方法是让一个 cronjob 在某个时间间隔(至少每分钟一次)执行 PHP 脚本(即 CakePHP shell)。在这里您可以在后台执行此类任务。

但是,后台作业会出现一些问题。你怎么知道他们什么时候失败了?你怎么知道什么时候需要重试?如果它没有在 cron 间隔内完成怎么办..会发生竞争条件吗?

正确但更复杂的设置是使用工作/消息队列系统。它们允许您更优雅地处理上述问题,但通常需要您在服务器上运行后台守护程序来捕获和处理任何传入的作业。

这种工作方式是,在您的代码中(当用户注册时)您将作业插入队列。队列守护程序立即获取作业(它不会按时间间隔运行,因此它总是在等待)并将其交给工作进程(例如 CakePHP shell)。它是即时的 - 如果你告诉它 - 它知道它是否有效,它知道它是否失败,如果你愿意,它可以重试,并且它不会意外地处理相同的工作两次。

其中有很多可用的,例如Beanstalkd、dropr、Gearman、RabbitMQ 等。还有一些 CakePHP 插件(不同年龄的)可以提供帮助:

我曾在 Beanstalkd(+ PHP Pheanstalk 库)和 CakePHP Queue 插件(上面的第一个)中使用 CakePHP。我必须赞扬 Beanstalkd(用 C 编写)非常轻量、简单和快速。但是,关于 CakePHP 开发,我发现该插件启动和运行速度更快,因为:

  • 该插件附带您开始使用的所有 PHP 代码。使用 Beanstalkd,您需要编写更多代码(例如轮询队列以查找作业的 PHP 守护程序)
  • Beanstalkd 服务器基础架构变得更加复杂。我必须为 dev/test/prod 安装多个 beanstalkd 实例,并安装 supervisord 来管理进程。
  • 开发/测试稍微容易一些,因为它是一个独立的 CakePHP + MySQL 解决方案。您只需输入cake queue add user signup 和cake queue runworker。

【讨论】:

    【解决方案2】:

    我能够从控制器/动作运行控制台,请参见下面的示例。

    App::uses('ShellDispatcher', 'Console');
    ...
    public function aco_sync() {
        $command = '-app '.APP.' AclExtras.AclExtras aco_sync -r adminControllers -p UserAdmin';
        $args = explode(' ', $command);
        $dispatcher = new ShellDispatcher($args, false);
        if($dispatcher->dispatch()) {
            $this->Session->flash('OK');
        } else {
            $this->Session->flash('Error');
        }
        return $this->redirect(array('action' => 'index'));
    }
    

    【讨论】:

      【解决方案3】:

      在 CakePHP-3 中,您可以从控制器中分派 shell,其操作与在 CakePHP-2 中几乎相同。文档没有提到这一点。

      // in your controller:
      $shell = new \Cake\Console\Shell;
      $shell->dispatchShell('shell_class param1 param2');
      // or how the docs suggest
      $shell->dispatchShell('shell_class', 'param1', 'param2');
      

      注意单元测试中的stdout和stderr。

      调度 shell 会使用 ConsoleLogger 开启 stdout 和 stderr 日志记录,如果您在 phpunit 测试的代码中有类似上述代码 sn-p 的内容,则会在控制台中为您提供所有日志记录。

      【讨论】:

      • 要使用除 main() 以外的方法,我必须使用:$shell->dispatchShell('classname', 'method', 'param1');
      【解决方案4】:

      一切皆有可能,但你为什么要这么做。如果你发现你需要在 shell 中做一些事情,而实际的应用程序看看使用 libs。

      您将代码粘贴到 lib 中,然后从您的应用程序和 shell 中调用该 lib。

      【讨论】:

      • 我可以让它在后台运行吗?这是一个可能需要几分钟的过程。在 CakePHP 中启动后台作业的首选方式是什么?
      • 我需要通过第三方 API 获取用户内容。如果用户有很多内容,则可能需要一段时间。
      • webjay 在 YourController::__destruct() 或 YourController::afterRender() 中执行,它们都是在内容被刷新到浏览器之后。
      • 其他人可能需要这个的原因:shell 控制台上的 PHP 版本已经过时(4.4),而在浏览器中它是 5.3 - 所以在控制台中脚本无法工作。
      【解决方案5】:
      function getEbayOrder(){
          $this->autoRender = false;
      
          App::import('Console/Command', 'AppShell');
          App::import('Console/Command', 'EbayShell');
      
          $job = new EbayShell();
          $job->dispatchMethod('get_orders');
      
          echo "REPONSE";
      }
      

      【讨论】:

      • 你忘记调用 $job->startup() 方法
      • 谢谢,这是为我做的!非常快速和简单,只需确保包含AppShell,如此处所示。我不需要startup() btw。
      【解决方案6】:

      如果这是初始化 AclExtras 最好的方法是:

      App::import('Console/Command', 'AppShell');
      App::import('Plugin/AclExtras/Console/Command', 'AclExtrasShell');
      
      $job = new AclExtrasShell();
      $job->startup();
      $job->dispatchMethod('aco_sync');
      

      但要避免这种情况,除非您无法运行控制台脚本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-19
        • 2015-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多