swoole 协程下粗略判断执行的代码是否可能存在阻塞

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。
提示:运行时请不要开启抢占式调度,有问题请留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值