在多个协程间共用同一个协程客户端

同步阻塞程序不同,协程是并发处理请求的,因此同一时间可能会有很多个请求在并行处理,一旦共用客户端连接,就会导致不同协程之间发生数据错乱。

错误的代码

$server	= new Swoole\Http\Server('127.0.0.1', 9501);

$server->on('Receive', function ($serv, $fd, $rid, $data) {
	$redis	= RedisFactory::getRedis();
	$result = $redis->hgetall('key');
	$resp->end(var_export($result, true));
});

$server->start();

class RedisFactory
{
	private static $_redis	= null;

	public static function getRedis()
	{
		if (null === self::$_redis) {
			$redis	= new \Swoole\Coroutine\Redis();
			$redis->connect('127.0.0.1', 6379);
			self::$_redis	= $redis;
		}
		return self::$_redis;
	}
}

正确的代码

基于SplQueue实现协程客户端的连接池,可以复用协程客户端,实现长连接。

$pool = new RedisPool();
$server = new Swoole\Http\Server('127.0.0.1', 9501);

$server->on('Request', function($req, $resp) use ($pool) {
	//从连接池中获取一个Redis协程客户端
    $redis = $pool->get();
	//连接失败
    if ($redis === false)
    {
        $resp->end("ERROR");
        return;
    }
	$result = $redis->hgetall('key');
	$resp->end(var_export($result, true));
	//释放客户端,其他协程可复用此对象
	$pool->put($redis);
});

$server->start();

class RedisPool
{
    protected $pool;

    function __construct()
    {
        $this->pool = new SplQueue;
    }

    function put($redis)
    {
        $this->pool->push($redis);
    }

    function get()
    {
        //有空闲连接
        if (count($this->pool) > 0)
        {
            return $this->pool->pop();
        }

        //无空闲连接,创建新连接
        $redis = new Swoole\Coroutine\Redis();
        $res = $redis->connect('127.0.0.1', 6379);
        if ($res == false)
        {
            return false;
        }
        else
        {
            return $redis;
        }
    }
}