swoole内多进程并发redis和mysql出现请求返回数据串包问题bug解决

swoole内多进程并发redis和mysql出现请求返回数据串包问题bug解决

问题:

    swoole实现tcp服务时,在并发情况下,不同进程下的redis和db的请求数据返回出现串包混乱问题

出现原因:

     swoole tcp服务启动前调用了 mysql 和 redis 查询配置信息,并把连接对象实例化缓存起来了,然后在后续tcp启动前没有清理这些实例化的连接,导致多个work进程之间共享了这些启动前实例化的连接实例,从而在不同进程间并发处理请求时请求返回数据串包返回异常。

问题解决前代码:

    # tcp服务启动代码
    defineGameServer($serverId);
    // 此处未清理连接实例
    $tcp = new \Dragonica\Game\Socket\GameServer(SELF_IP, GAME_LISTEN_PRIVATE_PORT, $config, $serverId);
    $tcp->init();
    $tcp->startServer();


    defineGameServer()
    {
        # 查询配置信息,调用了
        # c_redis_db 
        # c_mysql_db
    }

    # redis&myqsl部分代码:
    class c_redis_db
    {
        private static $instancesLocal = array();
        private static $instancesRemote = array();
        static function getRedisInstance($db = self::DEFAULT_DB, $reconnect = false)
        {
            if ($reconnect || !array_key_exists($db, self::$instancesLocal)) 
                $redis = self::connect(ACCOUNT_REDIS_SERVER, ACCOUNT_REDIS_PORT, $db);
                self::$instancesLocal[$db] = $redis;
            } else {
                $redis = self::$instancesLocal[$db];
            }
            return $redis;
        }
    }

    # mysql部分代码

    class c_mysql_db
    {
        private $mysql;
        var $host;
        var $port;
        var $user;
        var $pwd;
        var $db;
        private $connected = false;
        private $error;
        private $inTran;
        static $instances = array();
        static function DBConnect($type = self::GAME_DB)
        {
            if (!array_key_exists($type, self::$instances)) {
                $instance = new c_mysql_db($db['host'], $db['port'], $db['user'], $db['pwd'], $db['db']);
                self::$instances[$type] = $instance;
            }
            $instance = self::$instances[$type];

            $instance->connect();

            return $instance;
        }
    }

问题解决后代码:

    # tcp服务启动代码
    defineGameServer($serverId);

    ******* 新加代码 START********
    c_redis_db_account::clearInstance();
    c_redis_db::clearInstance();
    c_mysql_db::clearInstance();
    ******* 新加代码 END  ********
    ..........
    ..........
    # redis&myqsl部分代码:
    class c_redis_db
    {
        ...........
        static function clearInstance() {
            foreach (self::$instancesLocal as $local){
                try {
                    $local->close();
                    showLog('close redis account local', true);
                } catch (\Exception $e) {
                    showLog("redis close error account local;", true);
                }
            }
            foreach (self::$instancesRemote as $remote){
                try {
                    $remote->close();
                    showLog('close redis account remote', true);
                } catch (\Exception $e) {
                    showLog("redis close error account remote;", true);
                }
            }
            self::$instancesLocal = array();
            self::$instancesRemote = array();
        }
    }

    # mysql部分代码

    class c_mysql_db
    {
        .............
        static function clearInstance() {
            foreach(self::$instances as $instance) {
                $instance->mysql->close();
                $instance->connected = false;
            }
            self::$instances = array();
        }
    }
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 4
Jyunwaa

这不是你自己的原因吗?看标题还以为 Swoole 出BUG了,为了流量不择手段👍。

1个月前 评论
Patienter (楼主) 1个月前

不错,之前也遇到过,我还以为多个协程同时使用一个redis客户端导致串,原来是多进程用了同一个导致串了

1个月前 评论
Patienter (楼主) 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!