概述:
webman + console + redis-queue 尝试平滑重启或停止,队列监听的 worker 进程无法正常终止。
直接stop 会导致执行中的队列任务执行中断,可能会引发业务问题。
环境:
macOS 10.15  x86_64
php 7.3.33(NTS)
redis_version:6.0.8
composer 相关库及版本
"name": "workerman/webman-framework","version": "v1.4.10"
"name": "webman/console","version": "v1.2.18"
"name": "webman/redis-queue", "version": "v1.2.4"
"name": "workerman/redis-queue",  "version": "v1.0.10",测试相关源码 为减少篇幅 代码换行有删减
// 1. 参考 业务初始化文档 补充了工作进程信息打印
// see: https://www.workerman.net/doc/webman/others/bootstrap.html
<?php
namespace app\bootstrap;
use Webman\Bootstrap;
class MemReport implements Bootstrap {
    public static function start($worker) {
        $is_console = !$worker;
        if ($is_console) {  return; }
        if ($worker->name == 'monitor') {  return;   }
        \Workerman\Timer::add(10, function () use($worker) {
            echo $worker->name .':'. $worker->id .' memUsage:'. formatBytes(memory_get_usage()) . PHP_EOL;
        });
//...
//2.参考 redis-queue 文档,添加了队列任务 及 消费脚本
// see:  https://www.workerman.net/plugin/12
//2.1 任务添加脚本
<?php
defined('BASE_PATH') or define('BASE_PATH', dirname(__DIR__));
require_once BASE_PATH . '/vendor/autoload.php';
require_once BASE_PATH . '/support/bootstrap.php';
use Webman\RedisQueue\Redis;
// 队列名
$queue = 'send-mail';
// 数据,可以直接传数组,无需序列化
$data = ['to' => 'tom@gmail.com', 'content' => 'hello'];
/** @see \Webman\RedisQueue\RedisConnection::send() */
// 投递消息
$t = Redis::send($queue, $data);
var_dump($t);
// 投递延时消息
$t = Redis::send($queue, $data, 5);
var_dump($t);
//2.2 消费脚本
<?php
namespace app\queue\redis;
use support\Log;
use Webman\RedisQueue\Consumer;
use Workerman\Worker;
class TestConsumer implements Consumer {
    // 要消费的队列名
    public $queue = 'send-mail';
    // 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
    public $connection = 'default';
    // 消费
    public function consume($data)  {
        // 无需反序列化
        var_export($data); // 输出 ['to' => 'tom@gmail.com', 'content' => 'hello']
        Log::info('consume start...');
        sleep(10);
        $total = 0;
        for ($i = 0; $i < 1000; $i++) {
            $total += $i;
            usleep(5000);
        }
        Log::info('total:'.$total);
        //实际会有一些db相关操作
        sleep(3);
        var_export(['update consumer 111']);
        Log::info('consume done.');
    }
}测试流程概述
调整部分配置,方便验证
        config/server.php   count web服务进程数设置为2   stop_timeout 终止超时设置为 3
        config/plugin/webman/redis-queue/process.php  consumer count  消费进程数设置为 1
执行 任务投递脚本  向redis队列中添加一些待消费的任务消息
启动webman  `php webman start`
     观察控制台的打印信息,webman 及 队列监听进程均正常启动
     队列开始消费
尝试执行 `php webman stop -g`  或 `php webman restart -g`
    Workerman[webman] stop
    Workerman[webman] is gracefully stopping ...
    //...无法正常完成
描述
    webman 服务工作进程 正常终止
    队列worker进程 无法终止
    服务启动对应的控制台持续输出 `plugin.webman.redis-queue.consumer:0 memUsage:6.55MB`
以上,还请大佬们帮忙解惑
未提及 平滑重启 或 停止监听建议补充参数或变量
stop -g 或者 restart -g 原理是当进程内部所有连接对象都销毁时,执行退出。因为队列里有一个到redis的长连接,不会销毁,所以无法停止。也就是说有长连接类的应用不支持stop -g 或者 restart -g,包括队列
嗯, 那请教下 关于队列监听,在保障不中断消费中的任务 的前提下,要终止监听,有什么好的处理方案或者建议么。
翻了下 stop/restart 以及 consumer 的相关源码,目前还没找到什么调整方案
stop_timeout 设置的值大于最大消费时间就好了
重试了一下, stop_timeout 设置为30 > 20 (消费时间),
基于 stop / restart -g 无法使用,只能直接 stop或restart
测试验证 stop / restart 还是会直接终止 消费中的任务,似乎也不太适用安全终止监听的需求场景
我再看看有没有什么办法吧, 也可能担心是多余的 (安全终止监听 = 伪需求)
感谢大佬 :)
我们的
config/server.php/stop_timeout设置的是30s,大于消费的最大时间我们测试过,直接使用
php start.php restart -d重启webman服务,并不会直接中断正在消费的任务会等到任务消费完成后,才会安全重启服务(进程)
试了下 -d 后台运行的模式 还是不行,消费者 处理逻辑如下
日志文件里面 只有 consume start...
restart 和 stop 都是, stop_timeout 设置 时间30 还是 60 没差异
不清楚 是不是跟你的版本不一样 导致的, 也不知道是不是其他原因