【问题标题】:Execute shell from controller CakePHP 3.x从控制器 CakePHP 3.x 执行 shell
【发布时间】:2016-09-15 15:46:16
【问题描述】:

我在 CakePHP Shell 中有一个特定任务,它由 CRON 作业执行。但我希望用户能够随时从网络界面(如按钮或类似的东西)执行它。

所以,我的问题是,这可以从控制器执行 shell 吗?

在控制器中模拟它:

bin/cake MyShell

我知道在之前的 CakePHP 版本中是可能的,但在最新版本中我没有找到与此相关的内容。并且使用 exec("bin/cake MyShell") 对我来说真的很脏。

【问题讨论】:

  • 你需要一个队列作业,找到这个插件(可能有帮助):github.com/dereuromark/cakephp-queue
  • 是的,这是正确的做法。否则,如果您想进行同步。不要使用外壳,而是将代码放入业务类中,然后直接从控制器调用它。
  • @ka_lin,谢谢,我会检查这个插件!
  • @mark 是的,但问题是,该任务需要很长时间才能执行(可以达到 +/-45 百万)并且每次都中断 php 超时。所以我不能在控制器中做到这一点。顺便说一句,感谢您的工作,tinyauth 非常棒!

标签: php shell cakephp controller


【解决方案1】:

创建一个shell对象,调用它想要执行的任何函数

 $myShell = new \App\Shell\MyShell;
 $myShell->anyShellFun();

【讨论】:

  • 你仍然不应该这样做,那是完全不同的通信层,混合这些是一种糟糕的模式。
【解决方案2】:

为了从您的控制器函数调用 shell,您需要在控制器函数中执行此操作:

namespace App\Controller;

use App\Controller\AppController;
use Cake\Console\ShellDispatcher; //use this shell dispacher

/**
* Example Controller
*
*/
class ExampleController extends AppController
{

/**
 * Index method
 *
 * @return \Cake\Network\Response|null
 */
public function index()
{
    $shell = new ShellDispatcher(); //create object of your Shell Dispatcher
    $output = $shell->run(['cake', 'foo']); //here foo is your shell name

    if (0 === $output) {
        $this->Flash->success('Success from shell command.');
    } else {
        $this->Flash->error('Failure from shell command.');
    }

    return $this->redirect('/');
  }
}

希望这能回答您的问题,如果有任何问题,请发表评论。

谢谢。

【讨论】:

  • 可以异步运行吗? $output = $shell->run(['cake', 'foo']);
【解决方案3】:

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

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

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

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

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

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

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

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

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

其中有很多可用的,例如BeanstalkddroprGearmanRabbitMQ 等。还有一些 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 signupcake queue runworker

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-07
    • 2016-03-23
    相关资源
    最近更新 更多