swoole 协程下粗略判断执行的代码是否可能存在阻塞
个人的一些思路仅供参考
1.利用swoole 协程切换的特性。
2.开两个协程,一个执行检测代码,另外一个对时间等参数处理。
3.如果运行结束,耗时较高,切换次数为0,说明可能存在阻塞情况。
一、代码实现
<?php
use Co\Channel;
use Swoole\Coroutine;
use function Co\run;
class CoCheck
{
private ?\Closure $fun;
/**
* 上次执行时间
* @var int
*/
private int $lastTime = 0;
/**
* 开始时间
* @var int
*/
private int $startTime = 0;
/**
* 结果信息
* @var array
*/
private array $resList = [];
/**
* 执行大概多少行指令
* @var int
*/
private int $instructCount = 0;
/**
* 阶段
* @var int
*/
private int $stage = 0;
/**
* @var int
*/
private int $sleepMicrosecond = 10;
/**
* 配置待检测函数
* @param \Closure $fun
*/
public function underDetectionFunction(\Closure $fun)
{
$this->fun = $fun;
}
/**
* 设置检测协程睡眠时长
* @param int $num
*/
public function setSleepMicrosecond(int $num)
{
if ($num > 0) {
$this->sleepMicrosecond = $num;
}
}
/**
* 运行检测
*/
public function runCheck()
{
$this->lastTime = 0;
$this->startTime = 0;
$this->stage = 0;
$this->instructCount = 0;
$this->resList = [];
if ($this->sleepMicrosecond > 5000) {
$this->resList[] = [
"name" => "设置睡眠毫秒大于",
"value" => 5000
];
return ;
}
if (!$this->fun instanceof \Closure) {
$this->resList[] = [
"name" => "没有配置检测函数",
"value" => -1
];
return ;
}
if (Coroutine::getCid() > 0) {
$this->coRun();
} else {
run(function (){
$this->coRun();
});
}
}
/**
* 运行函数
*/
private function coRun()
{
$channel = new Channel(1);
Coroutine::create(function () {
$this->startTime = (int) $this->getMillisecond();
$this->stage = 1;
$fun = $this->fun;
try {
$fun();
} catch (\Throwable $e) {
$this->resList[] = [
"name" => "发生错误",
"info" => $e->getMessage()
];
} finally {
$this->stage = 2;
}
});
Coroutine::create(function() use ($channel) {
while (true) {
$lastTime = (int) $this->getMillisecond();
if ($this->stage == 2) {
$this->resList[] = [
"name" => "总耗时",
"timeOut" => $lastTime - $this->startTime
];
$channel->push(true);
break;
}
$this->instructCount++;
$startTime = $this->lastTime;
if ($startTime == 0) {
$startTime = $this->startTime;
}
$timeOut = $lastTime - $startTime;
if ($timeOut > ($this->sleepMicrosecond + 5)) {
$this->resList[] = [
"name" => "运行切换耗时超过阀值",
"timeOut" => $timeOut
];
}
$this->lastTime = $lastTime;
\Co::sleep($this->sleepMicrosecond * 0.001); //睡眠
}
});
$res = $channel->pop(6);
if (!$res) {
$this->resList[] = [
"name" => "已超时请kill掉进程",
"timeOut" => -1
];
}
}
/**
* 获取结果列表
* @return array
*/
public function getResList(): array
{
return $this->resList;
}
/**
* 获取切换运行统计
* @return int
*/
public function getInstructCount(): int
{
return $this->instructCount;
}
/**
* 获取毫秒
* @return int
*/
private function getMillisecond() :string
{
list($s1, $s2) = explode(' ', microtime());
return sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
}
}
二、代码调用
$check = new CoCheck();
$check->underDetectionFunction(function() {
sleep(1);//代码片段
});
$check->runCheck();//运行检测
var_dump($check->getResList());//获取运行信息
var_dump($check->getInstructCount());//获取切换次数
测试环境:PHP版本 7.4, swoole版本 4.6,系统 linux。
提示:运行时请不要开启抢占式调度,有问题请留言。