php 速率控制

第一个就是速率控制。某天看到了某个大佬的GO学习笔记里面提到了go的速率控制,根据x/time/rate包来实现的,看了会源码把他粗略的修改过来的

<?php
/**
 * Created by 993651481.
 * User: xydk936
 * Date: 2018/6/6
 * Time: 下午2:16
 */
//根据go的源码粗略的转过来的了,没做任何的上锁,只支持毫秒
//有bug暂时不解决
$limit = TimeHelper::Every(1000 * 60); //1分钟只让访问两次
$limiter = new Limiter($limit, 2);

$http = new swoole_http_server("127.0.0.1", 9501);

$http->on('request', function ($request, $response) use ($limiter) {
    //别问我为什么只会一次应为请求图标了~
    if ($limiter->Allow()) {
        $code = "pass";
    } else {
        $code = "stopped";
    }
    $response->end("<h1>{$code}</h1>");
});
echo "浏览器查看结果~" . PHP_EOL;
$http->start();


class Limiter
{
    private
        $last = 0,
        $limit,
        $tokens = 0,
        $burst;

    public function __construct(Limit $r, int $b)
    {
        $this->limit = $r;
        $this->burst = $b;
    }

    public function Allow(): bool
    {
        return $this->AllowN(TimeHelper::MilliSecond(), 1);
    }

    public function AllowN(int $now, int $n): bool
    {
        return $this->reserveN($now, $n, 0);
    }

    private function reserveN(int $now, int $n, float $maxFutureReserve): bool
    {
        $class = $this->advance($now);
        echo $class->now, " ", $class->last, " ", $class->tokens . PHP_EOL;
        $class->tokens -= floatval($n);

        $waitDuration = 0.0;
        if ($class->tokens < 0) {
            $waitDuration = $this->limit->durationFromTokens(-$class->tokens);
        }

        $ok = $n <= $this->burst && $waitDuration <= $maxFutureReserve;
        if ($ok) {
            $this->last = $class->now;
            $this->tokens = $class->tokens;
        } else {
            $this->last = $class->last;
        }
        return $ok;
    }

    private function advance(int $now): stdClass
    {
        $last = $this->last;
        if (TimeHelper::Before($now, $last)) {
            $last = $now;
        }

        $maxElapsed = $this->limit->durationFromTokens(floatval($this->burst) - $this->tokens);
        //echo $maxElapsed . PHP_EOL;
        $elapsed = $now - $last;
        //echo $elapsed . PHP_EOL;
        //echo $now . " " . $last . " " . $maxElapsed . " " . $elapsed . PHP_EOL;
        if ($elapsed > $maxElapsed || $elapsed == 0) {
            $elapsed = $maxElapsed;
        }
        $delta = $this->limit->tokensFromDuration($elapsed);
        //var_dump($this->limit->l);
        //echo $elapsed, "@", $maxElapsed, "@", $this->burst, "@", $this->tokens, "@", $delta, PHP_EOL;
        $tokens = $this->tokens + $delta;
        $burst = $this->burst;
        if ($tokens > $burst) {
            $tokens = $burst;
        }
        $class = new stdClass();
        $class->now = $now;
        $class->last = $last;
        $class->tokens = $tokens;
        //echo $now, ' ', $last, ' ', $maxElapsed, ' ', $elapsed, ' ', $tokens, PHP_EOL;
        return $class;
    }
}


class Limit
{
    public $l;

    public function __construct(float $v)
    {
        $this->l = $v;
    }

    public function durationFromTokens(float $tokens): float
    {
        $seconds = $tokens / $this->l;
        return $seconds * 1e3;
    }

    public function tokensFromDuration(int $d): float
    {
        return TimeHelper::Seconds($d) * $this->l;
    }
}


class TimeHelper
{
    static function Before(int $t, int $u)
    {
        return $t < $u || $t == $u && $t < $u;
    }


    static function Seconds(int $d)
    {
        $sec = intval($d / 1000);
        $nsec = $d % 1000;
        return $sec + floatval($nsec) / 1e3;
    }

    static function Every(int $interval): Limit
    {
        return new Limit(1 / self::Seconds($interval));
    }

    static function MilliSecond(): int
    {
        list($msec, $sec) = explode(' ', microtime());
        return (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
    }
}

最直观的速率控制莫非就是网页上了。网页上打开会请求图片资源这里说明下

转载于:https://my.oschina.net/u/1766862/blog/1826167

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值