zoukankan      html  css  js  c++  java
  • Spring Cloud微服务安全实战_4-10_用spring-cloud-zuul-ratelimit做限流

    本篇讲网关上的限流

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

    1,在网关项目里,引入限流组件的maven依赖:

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

    github也有相关配置说明:https://github.com/marcosbarbero/spring-cloud-zuul-ratelimit

    限流框架限流需要存一些信息,可以存在数据库里,也可以存在redis里,这里就用redis做实验,而且我看该组件的限流存储枚举类里,如果使用数据库保存限流信息,只能用JPA,不能用mybatis(可能歪果仁更喜欢JPA):

    public enum RateLimitRepository {
        REDIS,
        CONSUL,
        JPA,
        BUCKET4J_JCACHE,
        BUCKET4J_HAZELCAST,
        BUCKET4J_IGNITE,
        BUCKET4J_INFINISPAN,
    }

    限流yml配置:

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

    zuul:
      routes: #路由的配置是个Map,可以配置多个
        token:  #token结尾的请求,都转发到http://localhost:9090认证服务器地址
          url:  http://localhost:9090
        order:  #order结尾的请求,都转发到http://localhost:9060 顶单服务
          url:  http://localhost:9060
      sensitive-headers:  null  #设置敏感头设置为空,Authorization等请求头的请求,都往后转发
      ratelimit:
          key-prefix: rate
          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: 10 #时间窗口 (in seconds)
              type: ##根据什么控制流量,可以组合使用,如url、httpmethod组合,就会把 /orders的get和post请求分开处理
                - url
                - http_method
              #- user  #根据用户控制需要Security支持,(一般不用)
              #- origin #根据客户端的ip控制

    3,实验 

    1,分别启动 订单 、认证、网关 三个服务

     

     2,从网关获取一个token:

    OAuth2 password模式参数

     网关client的username,password

     3,拿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的生成规则:

    import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.RateLimitUtils;
    import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.properties.RateLimitProperties;
    import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.support.DefaultRateLimitKeyGenerator;
    import org.springframework.cloud.netflix.zuul.filters.Route;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * 自定义限流key生成规则,自定义限流规则
     */
    @Component
    public class MyKeyGen extends DefaultRateLimitKeyGenerator {
    
        public MyKeyGen(RateLimitProperties properties, RateLimitUtils rateLimitUtils){
            super(properties,rateLimitUtils);
        }
    
        @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);
        }
    }

    重写错误处理:

    package com.nb.security.filter;
    
    import com.marcosbarbero.cloud.autoconfigure.zuul.ratelimit.config.repository.DefaultRateLimiterErrorHandler;
    import org.springframework.stereotype.Component;
    
    /**
     * 限流错误自定义处理
     */
    @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);
        }
    }

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

    1,耦合

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

    2,限流的数量的问题

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

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

           

     本篇github :  https://github.com/lhy1234/springcloud-security/tree/chapt-4-10-ratelimit  如果对你有一点帮助,点个小星星吧!谢谢

  • 相关阅读:
    更改Linux时区的两种方法
    Azure上部署FTP服务
    log4j2简单使用
    elasticsearch5.5.2环境搭建
    springTask和Schedule学习
    防止SpringMVC拦截器拦截js等静态资源文件
    Mysql中使用FIND_IN_SET解决IN条件为字符串时只有第一个数据可用的问题
    ServletContext总结
    window.location.href和window.location.replace的区别
    javadoc文档
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/12127768.html
Copyright © 2011-2022 走看看