laravel框架使用gateway-client时,偶尔出现卡顿情况

mrwu

bug描述

laravel框架使用gateway-client时,调用isUidOnline等诸多方法时,偶尔会出现卡顿5秒的情况。
经过排查源码,发现是gatewayclient/Gateway.php这个文件的如下图方法中,陷入死循环,到了超时时间5秒后退出循环。不知道是什么原因导致死循环

程序代码或配置

//摘自源码
 protected static function getBufferFromGateway($gateway_buffer_array)
    {
        $client_array = $status_data = $client_address_map = $receive_buffer_array = $recv_length_array = array();
        // 批量向所有gateway进程发送请求数据
        foreach ($gateway_buffer_array as $address => $gateway_buffer) {
            $client = stream_socket_client("tcp://$address", $errno, $errmsg, static::$connectTimeout);
            if ($client && strlen($gateway_buffer) === stream_socket_sendto($client, $gateway_buffer)) {
                $socket_id                        = (int)$client;
                $client_array[$socket_id]         = $client;
                $client_address_map[$socket_id]   = explode(':', $address);
                $receive_buffer_array[$socket_id] = '';
            }
        }
        // 超时5秒
        $timeout    = 5;
        $time_start = microtime(true);
        // 批量接收请求
        while (count($client_array) > 0) {
            $write = $except = array();
            $read  = $client_array;
            if (@stream_select($read, $write, $except, $timeout)) {
                foreach ($read as $client) {
                    $socket_id = (int)$client;
                    $buffer    = stream_socket_recvfrom($client, 65535);
                    if ($buffer !== '' && $buffer !== false) {
                        $receive_buffer_array[$socket_id] .= $buffer;
                        $receive_length = strlen($receive_buffer_array[$socket_id]);
                        if (empty($recv_length_array[$socket_id]) && $receive_length >= 4) {
                            $recv_length_array[$socket_id] = current(unpack('N', $receive_buffer_array[$socket_id]));
                        }
                        if (!empty($recv_length_array[$socket_id]) && $receive_length >= $recv_length_array[$socket_id] + 4) {
                            unset($client_array[$socket_id]);
                        }
                    } elseif (feof($client)) {
                        unset($client_array[$socket_id]);
                    }
                }
            }
            if (microtime(true) - $time_start > $timeout) {
                break;
            }
        }
        $format_buffer_array = array();
        foreach ($receive_buffer_array as $socket_id => $buffer) {
            $local_ip                                    = ip2long($client_address_map[$socket_id][0]);
            $local_port                                  = $client_address_map[$socket_id][1];
            $format_buffer_array[$local_ip][$local_port] = unserialize(substr($buffer, 4));
        }
        return $format_buffer_array;
    }

期待的结果及实际结果

希望各位大佬帮忙看看,是什么原因导致进入死循环,有什么优化解决的方法。

重现bug的步骤

启动web服务后,不确定的时间之后,无论调用调用isUidOnline还是getAllClientIdCount 等方法,都会走到那个死循环5秒后超时退出。于此同时,另一个项目却可以正常的调用,调用的都是这同一个gateway-worker

系统环境及workerman/webman等具体版本

centos7
lumen5.8+laravelS加速
gatewayclient3.0.12
gatewayworker3.0.19

870 3 0
3个回答

mrwu

lumen通过laravelS加速后,视为常驻内存框架,不知道是不是这个原因?

  • 暂无评论
mrwu

个人还有个怀疑的地方,就是代码片段中
$buffer = stream_socket_recvfrom($client, 65535);
这一行中65535这个值,我调整过服务器的ulimit值,不知道是不是因为这个原因

  • 暂无评论
walkor

卡5秒是因为有gateway进程一直没返回数据

可能的原因有
1、gateway所在服务器没有安装event扩展(php start.php status 结果里需要有event字样),单个进程连接数超过1024时超出的连接无法响应,包括内部接口调用的连接。安装扩展后需要restart重启才能生效,reload不生效。
2、进程可打开文件数设置过低,当连接数超过进程可打开文件数后,一些连接可能无法响应数据。更改内核配置需要restart重启才能生效,reload不生效。
3、跨服务器的话也可能是网络抖动导致

  • 暂无评论
年代过于久远,无法发表回答
🔝