Swoole 协程使用注意事项
PHP Swoole 协程使用注意事项
近期公司的项目需要,用Swoole搭建TCP服务器,过程中使用了Coroutine\Redis,以及Coroutine\Mysql,在第一个版本中为了节省TCP及Redis的连接数,协程的理解的不够深入,多个协程使用了同一个Redis及Mysql客户端,导致不同的协程之间发生了数据错乱。
在此简要分析如下:
简单数据类型:
$number = 1;
go(function () use(&$number) {
Co::sleep(1);
$number++;
echo "In First Coroutine Number is:".$number;
});
$number++;
echo "In Main ".$number."\n";
go(function () use(&$number) {
$number++;
echo "In Second Coroutine Number is:".$number;
});
本来想获得顺序为
In First Coroutine Number is:2 In Main 3 In Second Coroutine Number is:4
运行此代码获得如下结果:
In Main 2 In Second Coroutine Number is:3 In First Coroutine Number is:4
示例很简单,但是展示了协程的乱序可能会导致的程序错误。
在调试过程中因为使用了单一连接客户端导致过:
Uncaught Swoole\Error: Socket#8 has already been bound to another coroutine
因此在swoole中使用协程时,一般使用协程客户端连接池。
示例代码如下:
/** * Class RedisPool */ class RedisPool { protected $mAvailable; protected $mPool; protected $mConfig;public function __construct($config) { $this->mAvailable = true; $this->mPool = new SplQueue(); $this->mConfig = $config; }
public function push($redisClient) { if ($this->mAvailable) { $this->mPool->enqueue($redisClient); } }
public function pop() { $redisClient = null; if ($this->mAvailable) { try { $redisClient = $this->mPool->dequeue(); } catch (\RuntimeException $exception) { //队列中为空 $redisClient = new RedisClient($this->mConfig); if (empty($redisClient) || $redisClient->connect()) { $redisClient = null; } } } return $redisClient; }
public function isAvailable() { return $this->mAvailable; }
public function setAvailable($available) { $this->mAvailable = $available; }
public function __destruct() { // 连接池销毁, 置不可用状态, 防止新的客户端进入常驻连接池, 导致服务器无法平滑退出 $this->mAvailable = false; while (!$this->mPool->isEmpty()) { $this->mPool->dequeue(); } }
public function __call($name, $arguments) { // TODO: Implement __call() method. if (!is_callable(array($this->mPool, $name))) { throw new BadFunctionCallException(“Function Name:” . $name . ” Args:” . implode(”,”, $arguments)); } return call_user_func_array([$this->mPool, $name], $arguments); }
在协程中,从连接池中获取连接客户端,保证每个协程使用不同的连接