一、swoole 协程并发调用封装简化使用流程
创建waitCo类是为了简化并发协程调用逻辑
1.参考WaitGroup 计数方式改造而来。
2.嵌套一层异常捕获,防止协程异常错误导致程序报错。
3.运行协程函数可以配置提前defer函数或finally 协程资源释放,防止忘记释放资源造成资源一直占用。
4.很方便的可以获取到协程运行时长。
5.异常收集方便快速找到问题。
<?php
use Swoole\Coroutine;
use Swoole\Coroutine\Channel;
/**
* Class WaitCo
* @package box\co
*/
class WaitCo
{
/**
* @var Channel
*/
protected Channel $channel;
/**
* 协程信息
* @var array
*/
protected array $coPool = [];
/**
* 等待标志
* @var int
*/
protected int $waiting = 0;
/**
* 错误信息
* @var array
*/
protected array $errorList = [];
/**
* 协程运行时长
* @var array
*/
protected array $coRunTime = [];
/**
* WaitCo constructor.
*/
public function __construct()
{
$this->channel = new Channel(1);
}
/**
* 创建协程
*/
public function createCo(callable $fun)
{
if ($this->waiting === 2) {
throw new \BadMethodCallException('WaitCo misuse: add called concurrently with wait');
}
if ($this->waiting === 3) {
$this->coRunTime = [];
$this->errorList = [];
}
$this->waiting = 1;
$cid = Coroutine::create(function () use ($fun) {
$cid = Coroutine::getCid();
try {
$fun();
}catch (\Throwable $e) {
$this->errorList[$cid] = ['code' => $e->getCode(), 'msg' => $e->getMessage(),'line' => $e->getLine()];
} finally {
$this->coRunTime[$cid] = $this->getMillisecond() - $this->coPool[$cid];
unset($this->coPool[$cid]);
//todo 释放协程内信息
if (count($this->coPool) == 0) {
$this->channel->push(true);
}
}
});
if ($cid > 0) {
$this->coPool[$cid] = $this->getMillisecond();
}
}
/**
* @param float|int $uTime
* @return bool
*/
public function wait(float $uTime = -1) : bool
{
if ($this->waiting !== 1) {
throw new \BadMethodCallException('WaitCo misuse: reused before previous wait has returned');
}
if (count($this->coPool) < 0) {
return true;
}
$cidList = array_keys($this->coPool);
foreach ($cidList as $val) {
if (!Coroutine::exists($val)) {
unset($this->coPool[$val]);
}
}
if (count($this->coPool) < 0) {
return true;
}
$this->waiting = 2;
$done = $this->channel->pop($uTime);
$this->waiting = 3;
return $done;
}
/**
* 获取毫秒
* @return string
*/
public function getMillisecond(): string
{
list($s1, $s2) = explode(' ', microtime());
return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
}
/**
* 获取错误信息
*/
public function getErrorList():array
{
return $this->errorList;
}
/**
* 获取运行时长
* @return array
*/
public function getTimeList():array
{
return $this->coRunTime;
}
}
二、调用
1.通过createCo方法执行协程函数。
2.在最终获取结果时通过wait方法等待协程执行完毕。
3.getErrorList 获取异常信息 注意返回的key为协程id。
4.getTimeList 获取运行时长(毫秒) 注意返回的key为协程id。
<?php
Coroutine\run(function () {
$result = [];//数据信息
$waitCo = new WaitCo();
$waitCo->createCo(function () use (&$result) {
//todo 请求http,获取链接池db或redis 调用数据库
Coroutine::sleep(1);
$result["test"] = "你好";
});
$waitCo->createCo(function () use (&$result) {
Coroutine::sleep(2);
$result['test2'] = "哈哈";
});
$waitCo->createCo(function () use (&$result) {
Coroutine::sleep(1);
throw new Exception("co error");
$result['exception'] = "test test";
});
$waitCo->wait();
var_dump($result);
var_dump($waitCo->getErrorList());
var_dump($waitCo->getTimeList());
});
三、模拟运行输出
1.通过例子中可以看到result 应该可以获取到test与test2信息,exception由于异常中断了赋值操作。
2.通过getErrorList可以获取到异常。
3.通过getTimeList获取运行时长。
array(2) {
["test"]=>
string(6) "你好"
["test2"]=>
string(6) "哈哈"
}
array(1) {
[4]=>
array(3) {
["code"]=>
int(0)
["msg"]=>
string(8) "co error"
["line"]=>
int(166)
}
}
array(3) {
[2]=>
int(1023)
[4]=>
int(1024)
[3]=>
int(2016)
}