如何在lumen里进行接口限频

作者: X先生说 | 来源:发表于2019-06-17 18:57 被阅读3次

    背景

    日常接口在使用过程中,如果不进行限频,很容易被突发异常的集中流量冲垮整个服务,故对web提供的接口都应该进行频率限制。

    laravel本身提供了限频的中间件ThrottleRequests,但是lumen框架里没有这个中间件,故这里说明下如何进行引入。

    引入流程

    • 新建文件 app/Http/Middleware/ThrottleRequests.php,文件内容为
    <?php
    
    namespace App\Http\Middleware;
    
    use App\Classes\Header;
    use Closure;
    use Carbon\Carbon;
    use Illuminate\Cache\RateLimiter;
    use Symfony\Component\HttpFoundation\Response;
    
    class ThrottleRequests
    {
        /**
         * The rate limiter instance.
         *
         * @var \Illuminate\Cache\RateLimiter
         */
        protected $limiter;
    
        /**
         * Create a new request throttler.
         *
         * @param  \Illuminate\Cache\RateLimiter $limiter
         */
        public function __construct(RateLimiter $limiter)
        {
            $this->limiter = $limiter;
        }
    
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request $request
         * @param  \Closure $next
         * @param  int $maxAttempts
         * @param  float|int $decayMinutes
         * @return mixed
         */
        public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
        {
            $key = $this->resolveRequestSignature($request);
    
            if ($this->limiter->tooManyAttempts($key, $maxAttempts, $decayMinutes)) {
                $this->buildResponse($key, $maxAttempts);
            }
    
            $this->limiter->hit($key, $decayMinutes);
    
            $response = $next($request);
    
            return $this->addHeaders(
                $response, $maxAttempts,
                $this->calculateRemainingAttempts($key, $maxAttempts)
            );
        }
    
        /**
         * Resolve request signature.
         *
         * @param  \Illuminate\Http\Request $request
         * @return string
         */
        protected function resolveRequestSignature($request)
        {
            //这里是生成请求的key,可以根据实际需求进行修改,Header一开始就把http header里的参数记录下来了,方便全局使用
            return sha1(
                $request->method() .
                '|' . $request->path() .
                '|' . Header::$ip .
                '|' . Header::$uid
            );
        }
    
        /**
         * Create a 'too many attempts' response.
         *
         * @param  string $key
         * @param  int $maxAttempts
         * @throws \Exception
         */
        protected function buildResponse($key, $maxAttempts)
        {
            $retryAfter = $this->limiter->availableIn($key);
    
            $msg = _('接口调用次数过多,请稍后重试:') . $retryAfter . 's';
    
            throw new \Exception($msg, 1);
        }
    
        /**
         * Add the limit header information to the given response.
         *
         * @param  \Symfony\Component\HttpFoundation\Response $response
         * @param  int $maxAttempts
         * @param  int $remainingAttempts
         * @param  int|null $retryAfter
         * @return \Symfony\Component\HttpFoundation\Response
         */
        protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
        {
            $headers = [
                'X-RateLimit-Limit' => $maxAttempts,
                'X-RateLimit-Remaining' => $remainingAttempts,
            ];
    
            if (!is_null($retryAfter)) {
                $headers['Retry-After'] = $retryAfter;
                $headers['X-RateLimit-Reset'] = Carbon::now()->getTimestamp() + $retryAfter;
            }
    
            $response->headers->add($headers);
    
            return $response;
        }
    
        /**
         * Calculate the number of remaining attempts.
         *
         * @param  string $key
         * @param  int $maxAttempts
         * @param  int|null $retryAfter
         * @return int
         */
        protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
        {
            if (is_null($retryAfter)) {
                return $this->limiter->retriesLeft($key, $maxAttempts);
            }
    
            return 0;
        }
    }
    
    • env文件里添加以下配置
    #cache
    CACHE_DRIVER=redis
    CACHE_PREFIX=your_prefix
    
    #redis
    REDIS_HOST=
    REDIS_PORT=6379
    #REDIS_PASSWORD=
    
    • bootstrap/app.php 增加中间件别名
    $app->routeMiddleware([
        'throttle' => App\Http\Middleware\ThrottleRequests::class,
    ]);
    
    • 路由进行限频设置
    $app->group([
        'middleware' => [
            'throttle:20,1'//1分钟限制20次
        ]
    ], function ($app) {
       
    });
    

    Enjoy it !
    如果觉得文章对你有用,可以赞助我喝杯咖啡~

    版权声明

    转载请注明作者和文章出处
    作者: X先生
    首发于 https://www.jianshu.com/p/3359cb311295

    相关文章

      网友评论

        本文标题:如何在lumen里进行接口限频

        本文链接:https://www.haomeiwen.com/subject/vumzfctx.html