概述:
webman + console + redis-queue 嘗試平滑重啟或停止,隊列監(jiān)聽的 worker 進程無法正常終止。
直接stop 會導(dǎo)致執(zhí)行中的隊列任務(wù)執(zhí)行中斷,可能會引發(fā)業(yè)務(wù)問題。
環(huán)境:
macOS 10.15 x86_64
php 7.3.33(NTS)
redis_version:6.0.8
composer 相關(guān)庫及版本
"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",
測試相關(guān)源碼 為減少篇幅 代碼換行有刪減
// 1. 參考 業(yè)務(wù)初始化文檔 補充了工作進程信息打印
// see: http://wtbis.cn/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 文檔,添加了隊列任務(wù) 及 消費腳本
// see: http://wtbis.cn/plugin/12
//2.1 任務(wù)添加腳本
<?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';
// 數(shù)據(jù),可以直接傳數(shù)組,無需序列化
$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';
// 連接名,對應(yīng) 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相關(guān)操作
sleep(3);
var_export(['update consumer 111']);
Log::info('consume done.');
}
}
測試流程概述
調(diào)整部分配置,方便驗證
config/server.php count web服務(wù)進程數(shù)設(shè)置為2 stop_timeout 終止超時設(shè)置為 3
config/plugin/webman/redis-queue/process.php consumer count 消費進程數(shù)設(shè)置為 1
執(zhí)行 任務(wù)投遞腳本 向redis隊列中添加一些待消費的任務(wù)消息
啟動webman `php webman start`
觀察控制臺的打印信息,webman 及 隊列監(jiān)聽進程均正常啟動
隊列開始消費
嘗試執(zhí)行 `php webman stop -g` 或 `php webman restart -g`
Workerman[webman] stop
Workerman[webman] is gracefully stopping ...
//...無法正常完成
描述
webman 服務(wù)工作進程 正常終止
隊列worker進程 無法終止
服務(wù)啟動對應(yīng)的控制臺持續(xù)輸出 `plugin.webman.redis-queue.consumer:0 memUsage:6.55MB`
以上,還請大佬們幫忙解惑
未提及 平滑重啟 或 停止監(jiān)聽
建議補充參數(shù)或變量
stop -g 或者 restart -g 原理是當進程內(nèi)部所有連接對象都銷毀時,執(zhí)行退出。因為隊列里有一個到redis的長連接,不會銷毀,所以無法停止。也就是說有長連接類的應(yīng)用不支持stop -g 或者 restart -g,包括隊列
嗯, 那請教下 關(guān)于隊列監(jiān)聽,在保障不中斷消費中的任務(wù) 的前提下,要終止監(jiān)聽,有什么好的處理方案或者建議么。
翻了下 stop/restart 以及 consumer 的相關(guān)源碼,目前還沒找到什么調(diào)整方案
重試了一下, stop_timeout 設(shè)置為30 > 20 (消費時間),
基于 stop / restart -g 無法使用,只能直接 stop或restart
測試驗證 stop / restart 還是會直接終止 消費中的任務(wù),似乎也不太適用安全終止監(jiān)聽的需求場景
我再看看有沒有什么辦法吧, 也可能擔心是多余的 (安全終止監(jiān)聽 = 偽需求)
感謝大佬 :)
我們的config/server.php/stop_timeout
設(shè)置的是30s,大于消費的最大時間
我們測試過,直接使用php start.php restart -d
重啟webman服務(wù),并不會直接中斷正在消費的任務(wù)
會等到任務(wù)消費完成后,才會安全重啟服務(wù)(進程)
試了下 -d 后臺運行的模式 還是不行,消費者 處理邏輯如下
Log::info('consume start...');
echo 'sleep...';
sleep(10);
Log::info('query db..');
$list = Db::table('admin_log')
->first();
Log::info('sleep again...');
sleep(10);
echo 'sleep done.';
Log::info('queryData:'.json_encode($list));
Log::info('consume done.');
日志文件里面 只有 consume start...
restart 和 stop 都是, stop_timeout 設(shè)置 時間30 還是 60 沒差異
不清楚 是不是跟你的版本不一樣 導(dǎo)致的, 也不知道是不是其他原因