概述:
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 没差异
不清楚 是不是跟你的版本不一样 导致的, 也不知道是不是其他原因