【发布时间】:2011-07-14 11:07:52
【问题描述】:
是否可以从 Controller 中使用 dispatchShell?
我的任务是在用户注册后启动一个 shell 作业。
我正在使用 CakePHP 2.0
【问题讨论】:
-
你可以参考我的帖子在这个链接上找到你的答案:stackoverflow.com/a/54801498/9406765
是否可以从 Controller 中使用 dispatchShell?
我的任务是在用户注册后启动一个 shell 作业。
我正在使用 CakePHP 2.0
【问题讨论】:
如果您无法按照教条建议的方式减少执行此操作的需要,请继续阅读。
因此,您有一个(可能)长时间运行的工作想要执行,并且您不希望用户等待。
由于您的用户正在执行的 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 开发,我发现该插件启动和运行速度更快,因为:
beanstalkd 实例,并安装 supervisord 来管理进程。cake queue add user signup 和cake queue runworker。【讨论】:
我能够从控制器/动作运行控制台,请参见下面的示例。
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'));
}
【讨论】:
在 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 的内容,则会在控制台中为您提供所有日志记录。
【讨论】:
$shell->dispatchShell('classname', 'method', 'param1');
一切皆有可能,但你为什么要这么做。如果你发现你需要在 shell 中做一些事情,而实际的应用程序看看使用 libs。
您将代码粘贴到 lib 中,然后从您的应用程序和 shell 中调用该 lib。
【讨论】:
function getEbayOrder(){
$this->autoRender = false;
App::import('Console/Command', 'AppShell');
App::import('Console/Command', 'EbayShell');
$job = new EbayShell();
$job->dispatchMethod('get_orders');
echo "REPONSE";
}
【讨论】:
AppShell,如此处所示。我不需要startup() btw。
如果这是初始化 AclExtras 最好的方法是:
App::import('Console/Command', 'AppShell');
App::import('Plugin/AclExtras/Console/Command', 'AclExtrasShell');
$job = new AclExtrasShell();
$job->startup();
$job->dispatchMethod('aco_sync');
但要避免这种情况,除非您无法运行控制台脚本。
【讨论】: