ac/fiendish-bundle

为您的 Symfony2 应用程序提供后台守护进程

0.2.6 2014-08-08 13:33 UTC

This package is not auto-updated.

Last update: 2024-09-24 00:25:29 UTC


README

Fiendish-bundle 允许您在 Symfony2 中编写守护进程,并控制它们的执行。

API 文档

安装

首先,您需要安装和设置这些非 PHP 依赖项

您还需要确保可以通过命令行访问 PHP 可执行文件。在 Ubuntu 上,这意味着安装 php5-cli 软件包,在其他发行版上可能类似。

接下来,使用 composer 将 fiendish-bundle 安装到您的 Symfony2 应用程序中

"require": {
    ...
    "americancouncils/fiendish-bundle": "dev-master"
}

并将 Fiendish 和 RabbitMQ 扩展包添加到您的 app/AppKernel.php 中的扩展包列表

new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
new AC\FiendishBundle\ACFiendishBundle()

然后,您需要在数据库中设置 Process 表。目前,这意味着手动安装和运行项目 DoctrineMigrations 文件夹中找到的迁移文件。

最后,您需要向 supervisor 添加一些设置以组织您特定应用程序的守护进程。以下是一个名为 Foobar 的项目的配置示例 /etc/supervisor/conf.d/foobar.conf

[program:foobar_master]
command=/usr/bin/php /var/www/foobar/app/console fiendish:master-daemon foobar
redirect_stderr=true

[group:foobar]

组部分故意留空;Fiendish 将会动态地向该组添加和删除进程。

您还需要在您的 Symfony 配置文件中添加相应的部分

fiendish:
    groups:
        foobar:
            process_user: "www-data"

process_user 是守护进程将运行的 UNIX 用户。

编写守护进程

守护进程作为从 BaseDaemon 继承的类实现。以下是一个 Foobar 的示例守护进程

namespace SomeRandomCoder\FoobarBundle\Daemon;

use AC\FiendishBundle\Daemon\BaseDaemon;

class UselessDaemon extends BaseDaemon
{
    public function run($arg)
    {
        while(true) {
            $this->heartbeat();

            print("FOO " . $arg['phrase'] . "!\n");
            sleep(1);
            print("BAR " . $arg['phrase'] . "!\n");
            sleep(1);
        }
    }
}

当守护进程启动时将调用 run 方法。如果您的守护进程需要一直运行,则 run 应该永不返回。它还需要定期调用 heartbeat 方法,每几秒钟调用一次。如果心跳之间的时间太长(默认为 30 秒),则假定守护进程已冻结并强制重启。

守护进程可以完全访问您的 Symfony 应用程序的服务。您可以通过调用 $this->getContainer() 来获取容器。

您还可以在启动守护进程时传递参数。任何可 JSON 序列化的对象都可以使用。在上面的示例中,传入了一个包含单个键的关联数组。

启动和停止守护进程进程

要启动守护进程,请使用以下示例中的 Group 服务

use SomeRandomCoder\FoobarBundle\Daemon\UselessDaemon;

$container = $this->getContainer();
$kernel = $container->get('kernel');
$group = $container->get('fiendish.groups.foobar');
$proc = $group->newProcess(
    "useless_thing", // Name prefix, to help identify this process
    UselessDaemon::toCommand($kernel), // The command to execute
    ["phrase" => "fries and a shake"] // The argument for run()
);
$procName = $proc->getProcName(); // Needed to access this Process later
$group->applyChanges(); // This call does not block

当调用 applyChanges 时,主守护进程会醒来,并将所有新进程添加到 Supervisor 组中并启动它们。

停止正在运行的守护进程的方式类似

$container = $this->getContainer();
$group = $container->get('fiendish.groups.foobar');
$proc = $group->getProcess($procName); // This is the procName you got earlier...
$group->removeProcess($proc);
$group->applyChanges();

调试

Supervisor 会跟踪您的守护进程打印的所有内容以及它们启动和停止的所有相关活动。确定您的守护进程有任何问题的最佳方法是使用 Supervisor 控制台

$ sudo supervisorctl
> status
foobar_master              RUNNING    pid 8263, uptime 1:35:03
foobar:useless_thing.373771873643687276    RUNNING   pid 8267, uptime 1:28:28
> tail -f foobar:useless_thing.373771873643687276
FOO fries and a shake!
BAR fries and a shake!
FOO fries and a shake!
BAR fries and a shake!
...  (All print output and PHP error output ends up here) ...

(幸运的是,Supervisor 的控制台具有自动完成功能,因此无需键入用于唯一标记进程的长随机数字。)

非 PHP 守护进程

您可以通过使用 ExternalDaemon 类在除 PHP 之外的语言中编写守护进程。实现 getExternalCommand 方法,返回可执行文件的资源路径或绝对路径

namespace JoeCoder\MyBundle\Daemon;

use AC\FiendishBundle\Daemon\ExternalDaemon;

class MyPythonAppDaemon extends ExternalDaemon
{
    public function getExternalCommand()
    {
        return "@JoeCoderMyBundle/Resources/scripts/myapp.py";
    }
}

传递给 startProcess 的第三个参数应该是一个数组,其内容将被作为命令行参数传递给您的程序。

您的守护进程仍然需要以固定间隔发射心跳。为了帮助实现这一点,设置了两个环境变量:

  • FIENDISH_HEARTBEAT_ROUTING_KEY
  • FIENDISH_HEARTBEAT_MESSAGE

要发射心跳,请将指定的消息发布到默认交换机,并在AMQP中使用指定的路由键。