Laraval Rate Limit

01 Aug 2019 Category:

layout: post title: Dingo Api 的限流在Laravel的限流基础上做了哪些修改? category: php comments: true description: Dingo Api 的限流在Laravel的限流基础上做了哪些修改 keywords: Dingo,Laravel,api限流 —

今天看文档的时候看到 Laravel的 节流限速 (throttling) 。网络上搜索,又看到了Dingo 的节流限速的文档。因此查看Laravel 与Dingo的源码,对比两者之间的相同点与不同点。

相同点

Laravel 限流中间件 Illuminate\Routing\Middleware\ThrottleRequests


public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
{
  $key = $this->resolveRequestSignature($request);

  $maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);

  if ($this->limiter->tooManyAttempts($key, $maxAttempts, $decayMinutes)) {
    throw $this->buildException($key, $maxAttempts);
  }

  $this->limiter->hit($key, $decayMinutes);

  $response = $next($request);

  return $this->addHeaders(
    $response, $maxAttempts,
    $this->calculateRemainingAttempts($key, $maxAttempts)
  );
}

Dingo 限流中间件Dingo\Api\Http\Middleware\RateLimit

public function handle($request, Closure $next)
{
  if ($request instanceof InternalRequest) {
    return $next($request);
  }

  $route = $this->router->getCurrentRoute();

  if ($route->hasThrottle()) {
    $this->handler->setThrottle($route->getThrottle());
  }

  $this->handler->rateLimitRequest($request, $route->getRateLimit(), $route->getRateLimitExpiration());

  if ($this->handler->exceededRateLimit()) {
    throw new RateLimitExceededException('You have exceeded your rate limit.', null, $this->getHeaders());
  }

  $response = $next($request);

  if ($this->handler->requestWasRateLimited()) {
    return $this->responseWithHeaders($response);
  }

  return $response;
}

不同点

Dingo\Api\Http\RateLimit\Handler代码如下:

$this->keyPrefix = sha1($request->path());
...
public function getRateLimiter()
{
  return call_user_func($this->limiter ?: function ($container, $request) {
    return $request->getClientIp();
  }, $this->container, $this->request);
}

Illuminate\Routing\Middleware\ThrottleRequests代码如下:

  protected function resolveRequestSignature($request)
  {
    if ($user = $request->user()) {
      return sha1($user->getAuthIdentifier());
    }
    if ($route = $request->route()) {
      return sha1($route->getDomain().'|'.$request->ip());
    }
  }

Dingo\Api\Http\RateLimit\Handler获取限制最少的限制器代码如下:

public function rateLimitRequest(Request $request, $limit = 0, $expires = 0)
{
  ...
  $this->throttle = $this->getMatchingThrottles()->sort(function ($a, $b) {
        return $a->getLimit() < $b->getLimit();
      })->first();
  ...
}

Dingo\Api\Http\RateLimit\Handler获取设置的返回头信息代码如下:

protected function getHeaders()
{
  return [
    'X-RateLimit-Limit' => $this->handler->getThrottleLimit(),
    'X-RateLimit-Remaining' => $this->handler->getRemainingLimit(),
    'X-RateLimit-Reset' => $this->handler->getRateLimitReset(),
  ];
}

Illuminate\Routing\Middleware\ThrottleRequests获取设置的返回头信息代码如下:


protected function getHeaders($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'] = $this->availableAt($retryAfter);
  }

  return $headers;
}

总结

两者实现原理相同,只是在细节上Dingo的功能更加强大。Dingo 限制粒度系,限制规则上,可扩展性,灵活性都比Laravel强。

评论