本篇讲网关上的限流

用开源项目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  如果对你有一点帮助,点个小星星吧!谢谢

 

 

欢迎关注个人公众号一起交流学习: