使用workerman测试gateawayworker连接过慢

lee_

使用的是官方的demo

客户端代码

require __DIR__ . '/workerman/Autoloader.php';
use Workerman\Worker;
use Workerman\Lib\Timer;
use Workerman\Connection\AsyncTcpConnection;
$worker = new Worker();
$worker->onWorkerStart = 'connect';
function connect(){
    static $count = 0;
    // 2000个链接
    if ($count++ >= 10000) return;
    // 建立异步链接
    $con = new AsyncTcpConnection('ws://127.0.0.1:3458');
    $con->onConnect = function($con) {
        // 递归调用connect
        connect();
    };
    $con->onMessage = function($con, $msg) {
        //echo "recv $msg\n";
    };
    $con->onClose = function($con) {
        echo "con close\n";
    };
    // 当前链接每10秒发个心跳包
    Timer::add(10, function()use($con){
        $con->send("ping");
    });
    $con->connect();
    echo $count, " connections complete\n";
}
Worker::runAll();

服务端代码

  public static function onConnect($client_id)
    {
        // 向当前client_id发送数据 
        Gateway::sendToClient($client_id, "Hello $client_id\r\n");
        // 向所有人发送
        Gateway::sendToAll("$client_id login\r\n");
    }

   /**
    * 当客户端发来消息时触发
    * @param int $client_id 连接id
    * @param mixed $message 具体消息
    */
   public static function onMessage($client_id, $message)
   {
        // 向所有人发送 
    //    Gateway::sendToAll("$client_id said $message\r\n");
   }

服务器8核
客户端与服务器在同一台服务器上 本机访问
gateaway进程8个 business24个
cpu占用一半 内存几乎没用
event内核已安装 linux内核已优化
onMessage里的广播没屏蔽前跑到两千多就卡了,屏蔽后1w连接需要6,7分钟才能完成
想请教下是原本就是这样还是我linux优化不到位

/etc/sysctl.conf配置

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1

vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time=120

# see details in https://help.aliyun.com/knowledge_detail/39428.html
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.conf.all.arp_announce=2

net.core.somaxconn= 65535
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_tw_recycle = 0
fs.file-max = 6815744
# see details in https://help.aliyun.com/knowledge_detail/41334.html
net.ipv4.tcp_max_tw_buckets = 20000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
kernel.sysrq=1
kernel.pid_max=3999999

ulimit -a

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63456
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 102400
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63456
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
2713 1 0
1个回答

walkor 打赏

广播Gateway::sendToAll是很耗时的操作,需要和所有gateway通讯,并且将数据发给所有在线连接,如果在线人数多会占用大量的cpu资源和带宽资源。所以不要轻易调用Gateway::sendToAll广播数据。

一般来说普通服务器包量极限大概在10W-20W/S左右,超过这个量服务器负载就很高了,会很慢。如果是多队列网卡可以成倍增加这个极限。

你的业务代码是在onConnect也就是在有客户端连接时调用Gateway::sendToAll广播。这样会导致随着连接越来越多最后连接速度越来越慢。原因主要是当连接很多时广播数据会造成大量的包量和流量,假设已经有1w个连接,每秒有10个新连接连上来,那就是每秒广播10次数据给1w个连接,每秒10W个数据包,假设每个数据包100字节,那么每秒产生的流量大约为 80Mb/s的数据。这么大的数量和包量一般的服务器已经是极限了。假设每个数据包是1000字节,那么流量直接干到800Mb,需要服务器有G口的带宽了。要知道一个普通机房出口带宽才10G-20G左右。

如果你在onMessage里也用Gateway::sendToAll广播,由于客户端是每10秒发送一个ping,当服务器有2000个连接时,也就是服务端每秒收到200个ping,然后每秒广播200次$client_id said $message。则每秒产生的包量为2000*200=20W个包,产生的流量大概150M左右。同样普通服务器支撑不了这么大的包量。

所以不是随便写几行代码就能支撑万人在线,当在线人数很多时要考虑的地方很多。最主要的是减少通讯请求量,尤其不要随便广播数据。

  • lee_ 2020-07-14

    我第一感觉也是包太多,后台看流量达到150m了 本还希冀于我优化没搞好。请问正常业务下统计在线人数给用户 还有用户发来消息,展示给所有人有什么好的方法么 直播间大概1w人吧

  • walkor 2020-07-14

    统计在线人数有专门的接口,但是你千万别有个新连接就调用接口统计一次并广播,那样你会看到数字一直在跳,而且浪费cpu和带宽。你可以在0号进程开个定时器10秒统计一次。如果人数很多,上万人,实际上不需要100%精确统计,误差1000都可以接受。超过1万人你可以显示为1.x万人,如果本次统计1.x与上次统计1.x相等,则可以省略一次广播推送。同理几千人在线的话,100内的误差忽略不记,显示为x千人。记住广播很耗费资源,能省就省。

  • lee_ 2020-07-14

    谢谢老大!

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