0
点赞
收藏
分享

微信扫一扫

如何在lumen里进行接口限频

背景

日常接口在使用过程中,如果不进行限频,很容易被突发异常的集中流量冲垮整个服务,故对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 !
如果觉得文章对你有用,可以赞助我喝杯咖啡~

版权声明

举报

相关推荐

0 条评论