spring-cloud-zuul-ratelimit

RateLimiter是Google开源的实现了令牌桶算法的限流工具(速率限制器)。

Spring Cloud Zuul RateLimiter结合Zuul对RateLimiter进行了封装,通过实现ZuulFilter提供了服务限流功能

官网:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

步骤

导入依赖

在zuul服务引入坐标

<dependency>
    <groupId>com.marcosbarbero.cloud</groupId>
    <artifactId>spring-cloud-zuul-ratelimit</artifactId>
    <version>2.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置限流策略

zuul:
  ratelimit:
    enabled: true #开启限流功能
    repository: redis #限流数据的存储方式
    default-policy-list:
      # 所有服务在3秒内只能有1次请求且所有请求时间总和不得超过2秒
      - limit: 1 #单位时间内请求次数限制
        quota: 2 #单位时间内累计请求时间限制(秒),非必要参数
        refresh-interval: 3 #单位时间(秒),默认60秒

spring:
  redis:
    host: 127.0.0.1
    port: 6379

处理返回信息

我们可能需要对触发限流的情况做监控、报警等,需要识别由限流导致的异常返回

  1. 推荐使用默认的 RateLimiterErrorHandler,直接写自己需要的处理就行
    • 注意:RateLimiterErrorHandler无法捕获到限流异常信息429 TOO_MANY_REQUESTS,也就不能自定义异常返回信息
  2. zuul触发限流后会抛出 http 429,可以针对这个错误码对response包装
  3. 也可以在全局异常处理中,判断出现的异常是否是 RateLimitExceededException
import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.RateLimitExceededException;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class ErrorHandlerController implements ErrorController {
    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping("/error")
    public ResponseEntity error() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ZuulException exception = (ZuulException) ctx.getThrowable();
        if (exception != null && exception.getCause() instanceof RateLimitExceededException) {
            // 自定义返回封装
            return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("请稍后再试~");
        }
        // 其他类型的eror
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("服务异常");
    }
}

配置

zuul:
  #路由、重试等zuul其他配置省略
  routes:

  #限流
  ratelimit:
    enabled: true #开启限流功能
    key-prefix: ${spring.application.name}-rate-limit #缓存key的前缀
    repository: REDIS #存储类型。可选REDIS、CONSUL、JPA等,老版本还有本地内存可选
    behind-proxy: true #开启则限流与业务访问是异步的,相当于rateLimitFilter先放过;默认是false
    policy-list: #指定服务策略
      myProject1:
        - limit: 5 #这种配置方式相当于:10分钟内允许5个请求访问/api/test/info接口
          refresh-interval: 10
          type:
            - url_pattern=/api/test/info
      myProject2:
        - limit: 3000
          refresh-interval: 1 #更常见的配置是这种,1秒允许3k个,相当于配qps限制
          type:
            - url_pattern=/api/test2/info        
        - limit: 300
          refresh-interval: 1 #如果同一个服务有多个需要限流的url,可以这样
          type:
            - url_pattern=/api/test2/info2

YOLO