PHP守护进程/工作者环境

时间:2009-04-15 15:22:11

标签: php parallel-processing daemon rabbitmq process-control

问题:我想实现几个正在侦听MQ服务器队列以进行异步作业的php-worker进程。现在的问题是,简单地将这个进程作为服务器上的守护进程运行并不能真正给我任何级别的控制(负载,状态,锁定)......除了可能用于转储ps -aux。 因此,我正在寻找一种允许我监视和控制实例的运行时环境,无论是在系统(进程)级别还是在更高层(某种Java风格的应用程序服务器)上

任何指针?

5 个答案:

答案 0 :(得分:12)

以下是一些可能有用的代码。

<?
define('WANT_PROCESSORS', 5);
define('PROCESSOR_EXECUTABLE', '/path/to/your/processor');
set_time_limit(0);
$cycles = 0;
$run = true;
$reload = false;
declare(ticks = 30);

function signal_handler($signal) {
    switch($signal) {
    case SIGTERM :
        global $run;
        $run = false;
        break;
    case SIGHUP  :
        global $reload;
        $reload = true;
        break;
    }   
}

pcntl_signal(SIGTERM, 'signal_handler');
pcntl_signal(SIGHUP, 'signal_handler');

function spawn_processor() {
    $pid = pcntl_fork();
    if($pid) {
        global $processors;
        $processors[] = $pid;
    } else {
        if(posix_setsid() == -1)
            die("Forked process could not detach from terminal\n");
        fclose(stdin);
        fclose(stdout);
        fclose(stderr);
        pcntl_exec(PROCESSOR_EXECUTABLE);
        die('Failed to fork ' . PROCESSOR_EXECUTABLE . "\n");
    }
}

function spawn_processors() {
    global $processors;
    if($processors)
        kill_processors();
    $processors = array();
    for($ix = 0; $ix < WANT_PROCESSORS; $ix++)
        spawn_processor();
}

function kill_processors() {
    global $processors;
    foreach($processors as $processor)
        posix_kill($processor, SIGTERM);
    foreach($processors as $processor)
        pcntl_waitpid($processor);
    unset($processors);
}

function check_processors() {
    global $processors;
    $valid = array();
    foreach($processors as $processor) {
        pcntl_waitpid($processor, $status, WNOHANG);
        if(posix_getsid($processor))
            $valid[] = $processor;
    }
    $processors = $valid;
    if(count($processors) > WANT_PROCESSORS) {
        for($ix = count($processors) - 1; $ix >= WANT_PROCESSORS; $ix--)
            posix_kill($processors[$ix], SIGTERM);
        for($ix = count($processors) - 1; $ix >= WANT_PROCESSORS; $ix--)
            pcntl_waitpid($processors[$ix]);
    } elseif(count($processors) < WANT_PROCESSORS) {
        for($ix = count($processors); $ix < WANT_PROCESSORS; $ix++)
            spawn_processor();
    }
}

spawn_processors();

while($run) {
    $cycles++;
    if($reload) {
        $reload = false;
        kill_processors();
        spawn_processors();
    } else {
        check_processors();
    }
    usleep(150000);
}
kill_processors();
pcntl_wait();
?>

答案 1 :(得分:4)

听起来你已经在* nix系统上运行了MQ,并且想要一种管理工作者的方法。

一种非常简单的方法是使用GNU屏幕。要启动10名工人,您可以使用:

#!/bin/sh
for x in `seq 1 10` ; do
screen -dmS worker_$x php /path/to/script.php worker$x
end

这将使用名为worker_1,2,3的屏幕等在后台启动10名工作人员。

您可以通过运行-r worker_屏幕重新连接到屏幕,并使用screen -list列出正在运行的worker。

有关详细信息,本指南可能会有所帮助: http://www.kuro5hin.org/story/2004/3/9/16838/14935

还可以尝试:

  • screen --help
  • man screen
  • google

对于生产服务器,我通常建议使用普通的系统启动脚本,但是我已经从启动脚本运行了多年的屏幕命令而没有任何问题。

答案 2 :(得分:1)

你真的需要它继续运行吗?

如果您只想根据请求生成新进程,可以在xinetd中将其注册为服务。

答案 3 :(得分:1)

用于PHP的pcntl插件类型服务器守护程序

http://dev.pedemont.com/sonic/

答案 4 :(得分:0)

Bellow是我们@chaos答案的工作实现。处理信号的代码被删除了,因为这个脚本通常只有几毫秒。

此外,在代码中我们添加了两个函数来保存调用之间的pid:restore_processors_state()和save_processors_state()。我们在那里使用过redis,但您可以决定在文件上使用实现。

我们每分钟使用cron运行此脚本。 Cron检查所有进程是否存活。如果不是 - 它重新运行它们然后死亡。如果我们想要杀死现有流程,那么我们只需使用参数kill运行此脚本:php script.php kill

在不将脚本注入init.d的情况下运行worker的非常方便的方法。

<?php

include_once dirname( __FILE__ ) . '/path/to/bootstrap.php';

define('WANT_PROCESSORS', 5);
define('PROCESSOR_EXECUTABLE', '' . dirname(__FILE__) . '/path/to/worker.php');
set_time_limit(0);

$run = true;
$reload = false;
declare(ticks = 30);

function restore_processors_state()
{
    global $processors;

    $redis = Zend_Registry::get('redis');
    $pids = $redis->hget('worker_procs', 'pids');

    if( !$pids )
    {
        $processors = array();
    }
    else
    {
        $processors = json_decode($pids, true);
    }
}

function save_processors_state()
{
    global $processors;

    $redis = Zend_Registry::get('redis');
    $redis->hset('worker_procs', 'pids', json_encode($processors));
}

function spawn_processor() {
    $pid = pcntl_fork();
    if($pid) {
        global $processors;
        $processors[] = $pid;
    } else {
        if(posix_setsid() == -1)
            die("Forked process could not detach from terminal\n");
        fclose(STDIN);
        fclose(STDOUT);
        fclose(STDERR);
        pcntl_exec('/usr/bin/php', array(PROCESSOR_EXECUTABLE));
        die('Failed to fork ' . PROCESSOR_EXECUTABLE . "\n");
    }
}

function spawn_processors() {
    restore_processors_state();

    check_processors();

    save_processors_state();
}

function kill_processors() {
    global $processors;
    foreach($processors as $processor)
        posix_kill($processor, SIGTERM);
    foreach($processors as $processor)
        pcntl_waitpid($processor, $trash);
    unset($processors);
}

function check_processors() {
    global $processors;
    $valid = array();
    foreach($processors as $processor) {
        pcntl_waitpid($processor, $status, WNOHANG);
        if(posix_getsid($processor))
            $valid[] = $processor;
    }
    $processors = $valid;
    if(count($processors) > WANT_PROCESSORS) {
        for($ix = count($processors) - 1; $ix >= WANT_PROCESSORS; $ix--)
            posix_kill($processors[$ix], SIGTERM);
        for($ix = count($processors) - 1; $ix >= WANT_PROCESSORS; $ix--)
            pcntl_waitpid($processors[$ix], $trash);
    }
    elseif(count($processors) < WANT_PROCESSORS) {
        for($ix = count($processors); $ix < WANT_PROCESSORS; $ix++)
            spawn_processor();
    }
}

if( isset($argv) && count($argv) > 1 ) {
    if( $argv[1] == 'kill' ) {
        restore_processors_state();
        kill_processors();
        save_processors_state();

        exit(0);
    }
}

spawn_processors();