SpringCloud微服务安全(三)网关安全 3-8 用开源项目spring-cloud-zuul-ratelimit 做网关上的限流

1. 网关的限流

用开源项目spring-cloud-zuul-ratelimit 做网关上的限流 (项目github:https://github.com/marcosbarbero/ 

1.1 引入限流组件依赖

在 this-security-gateway网关项目中引入限流组件的maven依赖

                <dependency>
			<groupId>com.marcosbarbero.cloud</groupId>
			<artifactId>spring-cloud-zuul-ratelimit</artifactId>
			<version>2.2.4.RELEASE</version>
		</dependency>

1.2 在网关项目yml配置里,配置限流相关配置

限流的信息,在redis是有有效期的,过了有效期会自动清除,所以要想在redis看到限流的相关信息,这里时间窗口就设置为 10 秒(刚开始我设置的是1秒,redis死活看不到限流信息,浪费了十几分钟时间)

zuul:
  routes: # 路由的配置是个map,可以配置多个
    token: #token结尾的请求,都转发到http://localhost:9090认证服务器地址
      url: http://localhost:9090
    order: #order结尾的请求,都转发到http://localhost:9070 订单服务
      url: http://localhost:9080
  sensitive-headers:  #设置敏感头设置为空,Authorization等请求头的请求,都往后转发
  ratelimit:
    enabled: true
    repository: REDIS
    default-policy-list:
      #    ########### 如下的配置就是说:每1秒内不能超过2个请求,2个请求时间加起来不能超过1秒(quota)############
      - limit: 2 #optional - request number limit per refresh interval window
        quota: 1 #optional - request time limit per refresh interval window (in seconds)
        refresh-interval: 60 #时间窗口 (in seconds)
        type: ##根据什么控制流量,可以组合使用,如url、httpmethod组合,就会把 /orders的get和post请求分开处理
          - url
          - httpmethod

因为zuul使用ratelimit限流时用 JPA 存储导致抛出 JPARepository 没有将Bean实例化存储到容器中的问题,这里选择使用Redis来存储相关数据。

1.3 测试网关限流1

从网关获取一个token后,拿token访问网关创建订单。手速点的快点,Http就会返回 429 状态码,表示过多的请求。

 看redis,已经有限流的信息了:

生成的限流的key是: rate:order:/orders:POST        rate是配置的限流信息的前缀,order是zuul转发的规则,/orders:POST 是配置的限流类型 url 和http_method的组合

当下一个请求过来的时候就会计算key,然后根据key去redis找,看当前的key已经过了多少个请求了,来判断这次请求能不能过。

大部分情况下,根据user 、origin、 url 、http_method已经满足需求了。有些特殊的场景,需要根据传过来的参数进行限流,比如有两种优惠券A、B,优惠券A业务简单,每秒能处理100个请求,优惠券B复杂,每秒能处理10个请求,此时自带的限流规则就不能满足需求了。限流归根揭底是根据key来限流的,所以此时就要自定义key的生成规则。

自定义key的生成规则:

/**
 * @ClassName MyKeyGen
 * @Description TODO  自定义限流key生成规则,自定义限流规则
 * @Author wushaopei
 * @Date 2021/5/5 11:25
 * @Version 1.0
 */
public class MyKeyGen extends DefaultRateLimitKeyGenerator {

    @Override
    public String key(HttpServletRequest request, Route route, RateLimitProperties.Policy policy) {
        //可以从route拿出路由信息,自定义key生成规则:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit#usage
        return super.key(request, route, policy);
    }

    public MyKeyGen(RateLimitProperties properties, RateLimitUtils rateLimitUtils) {
        super(properties, rateLimitUtils);
    }
}

重写错误处理:

/**
 * @ClassName MyRateLimitErrorHandler
 * @Description TODO
 * @Author wushaopei
 * @Date 2021/5/5 11:29
 * @Version 1.0
 */
@Component
public class MyRateLimitErrorHandler extends DefaultRateLimiterErrorHandler {
    //限流数据存时候报错了的处理,一般不覆盖
    @Override
    public void handleSaveError(String key, Exception e) {
        super.handleSaveError(key, e);
    }

    //限流取数据报错的处理,一般不覆盖
    @Override
    public void handleFetchError(String key, Exception e) {
        super.handleFetchError(key, e);
    }

    //限流错误处理,记日志等
    @Override
    public void handleError(String msg, Exception e) {
        super.handleError(msg, e);
    }
}

2. 都在网关上做限流是有问题的

2.1 耦合

  如上所述的根据不同的优惠券进行的限流,如果优惠券又多了种类型,就要重写网关的限流的代码,重写部署网关,这就是耦合,是不行的。服务和服务之间,以及服务和基础组件之间,一定要解耦。微服务场景下解耦是价值最大的事。一旦两个服务耦合在一块,一个变了,另一个也要跟着变。

2.2 限流的数量的问题

  网关只能处理从整个微服务外边进来的请求,并不处理微服务之间的调用。如,有A B两个微服务,A还调用了B服务,A、B服务每秒处理的请求最大都是100个,网关限流 A 100请求/秒   ,B 100请求/秒。此时有100个A请求到网关,100个B请求也到了网关,都通过了网关,而此时,100个到A的请求又请求了100次B,导致B服务不可用!

所以:在网关上不要做细粒度的限流,没有用,因为很多服务之间的调用,都不走网关。网关层面只根据硬件设备的处理能力做一些限流,如服务的节点用的是tomcat,根据每个tomcat的资源配置,计算能处理的多少并发请求,根据这个去限流。具体的方法,跟业务逻辑的耦合,都不要发生在网关上的限流逻辑里。这就是上边配置里说的,限流类型 user最好不在网关上用。

猜你喜欢

转载自blog.csdn.net/weixin_42405670/article/details/116381313
今日推荐