74

Swoole-2.1.2版本中我们将Server的进程管理模块封装成了PHP类,现在可以在PHP代码中使用Swoole的进程管理器了。

在实际项目中经常需要写一些长期运行的脚本,如基于rediskafkarabbitmq实现的多进程队列消费者,多进程爬虫等等。程序员需要使用pcntlposix相关的扩展库实现多进程编程,需要开发者具备深厚的Linux系统编程功底,否则很容易出现问题。

Swoole提供的进程管理器来自于Swoole\Server,经过大量生产项目验证,稳定性和健壮性都非常高。可大大简化多进程脚本编程工作。

一、 创建进程池

PHP代码中使用new Swoole\Process\Pool即可创建一个进程池,构造方法的第一个参数传入工作进程的数量。使用on方法设置WorkerStart即可在工作进程启动时执行指定的代码,可以在这里进行while(true)循环从redis队列中获取任务并处理。使用start方法启动所有进程,管理器开始进入wait状态。

$workerNum = 10;
$pool = new Swoole\Process\Pool($workerNum);

$pool->on("WorkerStart", function ($pool, $workerId) {
    echo "Worker#{$workerId} is started\n";
    $redis = new Redis();
    $redis->pconnect('127.0.0.1', 6379);
    $key = "key1";
    while (true) {
         $msgs = $redis->brpop($key, 2);
         if ( $msgs == null) continue;
         var_dump($msgs);
     }
});

$pool->on("WorkerStop", function ($pool, $workerId) {
    echo "Worker#{$workerId} is stopped\n";
});

$pool->start();

使用进程管理器,可以保证工作进程的稳定性。

  • 某个工作进程遇到致命错误、主动退出时管理器会进行回收,避免出现僵尸进程
  • 工作进程退出后,管理器会自动拉起、创建一个新的工作进程

二、信号处理

Swoole进程管理器自带了信号处理,向管理器进程发送:

  • SIGTERM信号:中止服务,向所有工作进程发送SIGTERM关闭进程
  • SIGUSR1信号:重启工作进程,管理器会逐个重启工作进程

在工作进程中可以配合使用pcntl_signalpcntl_signal_dispatch实现信号处理。

$pool->on("WorkerStart", function ($pool, $workerId) {
    $running = true;
    pcntl_signal(SIGTERM, function () use (&$running) {
        $running = false;
    });
    echo "Worker#{$workerId} is started\n";
    $redis = new Redis();
    $redis->pconnect('127.0.0.1', 6379);
    $key = "key1";
    while ($running) {
         $msgs = $redis->brpop($key, 2);
         pcntl_signal_dispatch();
         if ( $msgs == null) continue;
         var_dump($msgs);
     }
});

三、任务投递

Swoole进程管理器自带了消息队列和TCP-Socket消息投递的支持。可设置监听系统队列或者TCP端口,接收任务数据。此项功能是可选的,要使用任务投递功能,需要对进程池对象设置onMessage回调。

消息队列

$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_MSGQUEUE, 0x7000001);

$pool->on("WorkerStart", function ($pool, $workerId) {
    echo "Worker#{$workerId} is started\n";
});

$pool->on("Message", function ($pool, $message) {
    echo "Message: {$message}\n";
});

$pool->start();

需要在构造方法的第二个参数传入SWOOLE_IPC_MSGQUEUE,第三个参数设置监听的消息队列KEY。其他程序中使用消息队列相关API就可以向工作进程投递任务了。

TCP 端口

$pool = new Swoole\Process\Pool(2, SWOOLE_IPC_SOCKET);

$pool->on("WorkerStart", function ($pool, $workerId) {
    echo "Worker#{$workerId} is started\n";
});

$pool->on("Message", function ($pool, $message) {
    echo "Message: {$message}\n";
});

$pool->listen('127.0.0.1', 8089);

$pool->start();

使用TCP端口监听,需要设置构造方法的第二个参数为SWOOLE_IPC_SOCKET,并使用listen方法设置监听的主机和端口。

底层使用了4字节长度+包体的协议。其他程序中向此端口发送数据时,需要在数据包之前增加一个长度字段。

$fp = stream_socket_client("tcp://127.0.0.1:8089", $errno, $errstr) or die("error: $errstr\n");
$msg = json_encode(['data' => 'hello', 'uid' => 1991]);
fwrite($fp, pack('N', strlen($msg)).$msg);
fclose($fp);
10 条评论
sunwenzheng · 4月11日

好文,希望多写一些

回复

Marico · 4月11日

稳稳的先占一个评论

回复

takashiki · 4月11日

请问 2.1.2 算 stable 版本么?2.0.12 是 stable 版本么?
因为官方文档上面 https://wiki.swoole.com/wiki/...,建议使用版本里稳定版写的是 1.10.2,而 github 上的 tag 里只有 2.0.10 有 stable 的标签。
而 2.0.11 版本中修复了一个 Coroutine::call_user_func_array 调用实例自身 protected 方法报错的 bug,所以我想请问下 2.0.12 版本是否为可以用于生产环境的稳定版?

回复

0

使用 2.1.2 版本

韩天峰 作者 · 4月11日
0

@韩天峰 好的,谢谢

takashiki · 4月11日
蓝天救援队 · 4月11日

超级实用!

回复

tom鼠 · 4月11日

正在学习swoole,非常不错

回复

伟大的脚丫 · 4月11日

kill掉主进程,进程池中的进程好像不会停止。

回复

0

直接 kill -9 强行结束主进程是不会停止其他进程的, 因为主进程和其他进程本质上没有依赖。 只有通过 kill -15 或者 kill 不加参数的方式,通知主进程,才能停止其他进程

svenhe · 4月13日
singwa · 4月11日

大赞峰哥

回复

载入中...