webman明明还有空闲子进程,为什么接收到新请求时却分配不到空闲进程

193796273@qq.com

问题描述

webman明明还有空闲子进程,为什么接收到新请求时却分配不到空闲进程?

进程(webman子进程)
客户端(发起请求的IP+发起请求时所使用的端口号)

经过测试发现一个现象,webman在处理请求时,似乎会把该进程和客户端做一个绑定关系,该客户端下次发起请求时,webman会用该客户端上一次使用过的子进程进行处理;

这样就会出现一个问题,可能这一个子进程绑定了多个客户端,假设该子进程在处理某一次请求时耗时30秒钟,在此期间与该子进程绑定的客户端发起新的请求就都会阻塞住(直到30秒钟后才能响应请求,因为此时子进程处于阻塞状态),但是此时还有很多其它的空闲子进程,要怎样才能把请求分配给空闲子进程处理呢?(就像php-fpm那样,一个子进程只处理一个请求,请求结束后进程销毁,这样就不会因为一个请求影响到另外一个请求)

同时发起10个请求,其中端口42610发起的请求被分配到了子进程12086上面(12806进程此时还在处理42612端口的请求)所以此时42610只能等待12806进程处理完42612的请求,30秒后,42610发起的请求得到响应
截图

请求头
截图

响应头
截图

Nginx代理配置

      location ~* /v2 {
        proxy_buffering on;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $host;
        proxy_buffer_size 256k;
        proxy_buffers 64 128k;
        proxy_busy_buffers_size 512k;
        proxy_temp_file_write_size 256k;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_read_timeout 600;
        proxy_connect_timeout 600;
        proxy_send_timeout 600;
        proxy_pass http://172.19.0.2:8787;
        #proxy_pass http://webman1;
        rewrite  "^/v2/(.*)$" /$1;
        break;
     }

测试模拟业务耗时20秒,开启10个进程并发请求,一共发起2轮请求

//模拟业务耗时场景
public function test(Request $request)
{
    Log::info(['runTest' => posix_getpid(), 'port' => $request->getRemotePort(), 'time' => time()]);
    Db::select('select sleep(20);');
    return json(['data' => date('Y-m-d H:i:s')]);
}

截图

2023-04-03 通过作者的帮助问题得到解决

1、自定义进程中添加一个处理请求进程(config/process.php)

<?php
return [
    'server' => [
        'handler'     => \app\Server::class,
        'listen'      => 'http://0.0.0.0:8686',
        'count'       => 500, // 启动进程数
        'constructor' => [
            'request_class' => \support\Request::class, // request类设置
            'logger'        => \support\Log::channel('default'), // 日志实例
            'app_path'      => app_path(), // app目录位置
            'public_path'   => public_path() // public目录位置
        ]
    ]
];

2、处理请求类源码(app/server.php)

<?php

namespace app;

use Webman\App;
use Workerman\Connection\TcpConnection;

class Server extends App
{
    public function onConnect(TcpConnection $connection)
    {
        $connection->worker->pauseAccept();
    }

    public function onMessage($connection, $request)
    {
        parent::onMessage($connection, $request);
        $connection->worker->resumeAccept();
    }

    public function onClose($connection)
    {
        $connection->worker->resumeAccept();
    }
}

3、把原来执行8787端口的请求指向8686

经过测试,已经达到目的,最大并发量等于启动的server进程数,并发高的时候增加config/process.php 里server进程的count启动进程数量就行

1157 4 7
4个回答

walkor

客户端连接到某个进程后,这个连接上发的数据都会发给这个进程。
如果你不需要这种机制,可以在webman前面放一个nginx代理,nginx代理不开启keep-alive。或者客户端请求时加一个header Connection: close 也可以

关于 请求集中在某些进程 参考手册
https://www.workerman.net/doc/workerman/faq/requests-concentrated-in-certain-processes.html

  • 193796273@qq.com 2023-04-02

    感谢大佬的答惑,经过测试,好像还是没有用,不知道是不是我哪一步弄错了,麻烦帮忙再看看,相关贴图在上方

  • walkor 2023-04-02

    如果你压测过程是先并发发起连接,然后再发请求,得用nginx代理才行。

  • 193796273@qq.com 2023-04-02

    目前的确是使用的Nginx代理做了一次转发的,但是可能我哪一步做的不对,上方贴图Nginx代理配置块,麻烦帮忙看看是否有错误

  • walkor 2023-04-02

    手册这个配置 https://www.workerman.net/doc/webman/others/nginx-proxy.html
    keepalive 10240; 去掉

  • 193796273@qq.com 2023-04-02

    经过测试,发现还是没有效果,此次测试模拟了业务耗时20秒,开了10个进程并发请求,一共请求2轮,结果还是跟最初的一样,贴图上方,麻烦帮忙再看看

  • walkor 2023-04-02

    如果你的并发数小于进程数,可以开启config/server.php中的reusePort

  • 193796273@qq.com 2023-04-02

    大佬你好,我之前就已经把reusePort设置为true了,但是经过测试还是不能达到我想要的效果

  • walkor 2023-04-02

    reusePort设置为true,然后nginx关闭keepalive

  • 193796273@qq.com 2023-04-02

    reusePort现在已经设置为true了,Nginx也关闭keepalive了(按你前面说的把keepalive 10240;这行配置删除了)webman和Nginx都重启过,难道是因为我还有别的Nginx配置项会影响到吗?不知道大佬方不方便微信联系,希望能提供下付费支持,解决我这个问题。

army

workerman里是开启端口复用 reusePort=true,webman应该也有类似配置,我们之前也遇到过这个问题。

  • walkor 2023-04-02

    webman里config/server.php里可以设置reusePort。
    不过reusePort 是让连接平均分配给所有进程,不是题主要的效果。

  • army 2023-04-02

    题主的问题不像是长连接导致倒是像未开启reusePort,我们起初也错误的使用$conn->send来发送导致全是长连接,但开启了多进程端口复用,也能扛得住的啊 哈哈

  • walkor 2023-04-02

    题主要的效果是A进程在处理慢业务,那么就不分配请求给A进程。
    如果是开长连接+reusePort,每个进程都会有多个连接,A进程在处理慢业务,那么这个时候A进程的它连接可能会发来请求,这些请求都会等待慢业务处理完毕才能得到处理。

  • 193796273@qq.com 2023-04-02

    是的,需要达到的目的就是只要还有空闲进程,那新进来的请求就一定能够得到处理,而不是分配到一个繁忙的进程上等待处理,目前我们项目已经上线运行大半年了,但是最近流量大起来,发现扛不住大并发的场景,现在很着急如何解决这样的问题

  • 193796273@qq.com 2023-04-03

    大哥牛逼呀!作者大哥就是这样教我处理的

ichynul

https://www.workerman.net/plugin/113
多端口并使用nginx做负载均衡,试一下效果怎么样。

  • 193796273@qq.com 2023-04-03

    感谢你的建议,按目前情况看,以这样的方式应该是可以缓解问题的

wadeYang

有标准解决方案吗?这个问题很经典,也很普遍,项目中可能遇到就是致命的问题,希望能共享解决方案

  • 193796273@qq.com 2023-04-03

    问题已经通过作者的帮助得到解决,解决方案已经贴在本帖最后了

年代过于久远,无法发表回答
🔝