问题描述
如图,calltask_init是异步任务服务端用来执行耗时任务,text协议,进程数开12,async_task_proxy为异步任务客户端,websocket协议,进程数开1,我在controller里做http接口,触发异步任务时直接连接async_task_proxy并指定要执行的异步任务。
结果我连接调接口8次,却只有5个任务开始执行了,另外3个任务为等待状态,具体哪个任务执行,目前没发现规律。
我希望得到的结果是:异步任务进程数开12的话,那么只有第13个调用时才会发生任务等待。
运行环境
cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
Workerman version:4.0.33 PHP version:7.4.28
{
"name": "workerman/webman-framework",
"version": "v1.3.9",
}
重现步骤
class TaskInit
{
//...
public function onMessage(TcpConnection $connection, $data)
{
$workerId = $connection->worker->id;
// 只接收json字符串
$data = json_decode($data, true);
$taskService = new \app\service\CallTask();
// 调\app\service\CallTask中的init方法(耗时)
$taskRes = $taskService->init($data['task_id']);
$connection->send(json_encode($taskRes, JSON_UNESCAPED_UNICODE));
}
//...
}
class AsyncTaskProxy
{
// ...
public function onMessage(TcpConnection $connection, $data)
{
$workerId = $connection->worker->id;
var_dump("proxy: " . $data);
// 接收客户端发来的指令
$data = json_decode($data, true);
if (isset($data['command']) && isset($data['payload'])) {
switch ($data['command']) {
case 'init':
// 指令为“初始化呼叫任务”
$taskId = $data['payload']['task_id'];
$taskConnection = new AsyncTcpConnection('text://127.0.0.1:9611');
$taskData = array(
'task_id' => $taskId,
);
$taskConnection->send(json_encode($taskData, JSON_UNESCAPED_UNICODE));
$taskConnection->onMessage = function (AsyncTcpConnection $taskConnection, $taskRes) use ($connection) {
var_dump('ws代理中的onMessage:');
var_dump($taskRes);
$taskConnection->close();
$connection->send($taskRes);
};
$taskConnection->connect();
break;
}
}
}
// ...
}
/* process.php */
return [
// ...
'calltask_init' => [
// rabbitmq的消费者示例
'handler' => process\TaskInit::class,
// 监听的协议 ip 及端口 (可选)
'listen' => 'text://0.0.0.0:9611',
// 进程数 (可选,默认1)
'count' => 12,
// 是否开启reusePort (可选,此选项需要php>=7.0,默认为true)
'reusePort' => true,
],
'async_task_proxy' => [
// rabbitmq的消费者示例
'handler' => process\AsyncTaskProxy::class,
// 监听的协议 ip 及端口 (可选)
'listen' => 'websocket://0.0.0.0:9608',
// 进程数 (可选,默认1)
'count' => 1,
// 是否开启reusePort (可选,此选项需要php>=7.0,默认为true)
'reusePort' => true,
]
];
感谢walkor老大的及时回复
通过在TaskInit的onConnect中打印posix_getpid()确实发现了问题:
发起9次请求,其中3次并没有触发onConnect方法。
我也继续排查一下。
给TaskInit 进程设置一个onConnect方法,onConnect里打印下进程pid,也能看出来连接的进程pid。
或者
用命令
netstat -nt | grep ESTABLISHED | grep 9611
能找出连接的本地端口,然后用命令
lsof -i:本地端口
能找到连接的是哪个进程 pid目前还没有找到解决的办法,已知有些异步任务被分配到了正在忙碌中的进程中了,而同时是存在空闲进程的。
calltask_init 进程把
'reusePort' => true,
改成'reusePort' => false,
试下确实reusePort设置为false后问题就解决了,只不过我还没想明白为什么,复用端口设置为false,就会优先选择空闲进程,而为true时就会优先选择已经连接上进程。
reusePort 为 true,由linux内核分配连接到进程,可能你的linux内核没有做到100%平均分配。
reusePort为false,哪个进程空闲,哪个进程去认领连接