SpringCloud-Zuul网关 自定义ZuulFilter过滤器实现 接口限流

在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流

缓存 缓存的目的是提升系统访问速度和增大系统处理容量
降级 降级是当服务出现问题或者影响到核心流程时,需要暂时屏蔽掉,待高峰或者问题解决后再打开
限流 限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理
 

 

常用的限流算法

漏桶算法
        漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

SpringCloud-Zuul网关 自定义ZuulFilter过滤器实现 接口限流_第1张图片


令牌桶算法
        对于很多应用场景来说,除了要求能够限制数据的平均传输速率外,还要求允许某种程度的突发传输。这时候漏桶算法可能就不合适了,令牌桶算法更为适合。如图所示,令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

SpringCloud-Zuul网关 自定义ZuulFilter过滤器实现 接口限流_第2张图片


RateLimiter使用
Google开源工具包Guava提供了限流工具类RateLimiter,该类基于令牌桶算法实现流量限制,使用十分方便,而且十分高效。

首先简单介绍下RateLimiter的使用

public void testAcquire() {
      //创建一个限流器,参数代表每秒生成的令牌数
      RateLimiter limiter = RateLimiter.create(1);

      for(int i = 1; i < 10; i = i + 2 ) {
          //以阻塞的方式获取令牌
          double waitTime = limiter.acquire(i);

          //来设置等待超时时间的方式获取令牌,如果超timeout为0,则代表非阻塞,获取不到立即返回。
          //limiter.tryAcquire(int permits, long timeout, TimeUnit unit);

          System.out.println("cutTime=" + System.currentTimeMillis() + " acq:" + i + " waitTime:" + waitTime);
      }
  }

输出结果

cutTime=1535439657427 acq:1 waitTime:0.0
cutTime=1535439658431 acq:3 waitTime:0.997045
cutTime=1535439661429 acq:5 waitTime:2.993028
cutTime=1535439666426 acq:7 waitTime:4.995625
cutTime=1535439673426 acq:9 waitTime:6.999223

通过上面的铺垫了解,下面正试开始用Zuul网关结合Guava实现对订单接口的流量限制
Zuul 网关服务项目的环境搭建,
可参考博文 https://blog.csdn.net/qq_41712271/article/details/104827839
项目核心代码,Zuul过滤器代码

import com.google.common.util.concurrent.RateLimiter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

/**
 * 订单限流
 */
@Component
public class OrderRateLimiterFilter extends ZuulFilter {

    //创建一个限流器,参数代表每秒生成的令牌数
    private static final RateLimiter RATE_LIMITER = RateLimiter.create(500);

    /**
     * 过滤器类型
     * org.springframework.cloud.netflix.zuul.filters.support.FilterConstants 这个类
     * pre:在请求被路由(转发)之前调用
     * route:在路由(请求)转发时被调用
     * error:服务网关发生异常时被调用
     * post:在路由(转发)请求后调用
     * @return
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 过滤器顺序,越小越先执行
     * @return
     */
    @Override
    public int filterOrder() {
        return -4;
    }


    /**
     * 过滤器是否生效
     * @return
     */
    @Override
    public boolean shouldFilter() {

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();

        //只对订单接口限流,进行拦截,就会进入下面的 run方法中
        if ("/apigateway/order/api/v1/order/save".equalsIgnoreCase(request.getRequestURI())){
            return true;
        }
        //不拦截,放行
        return false;
    }

    @Override
    public Object run() throws ZuulException {
        //获取上下文
        RequestContext requestContext = RequestContext.getCurrentContext();
        //尝试获取令牌
        if(!RATE_LIMITER.tryAcquire()){
            //停止访问,并返回出错的消息
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(HttpStatus.TOO_MANY_REQUESTS.value());
        }
        //正常的话,继续向下走
        return null;
    }
}

 

你可能感兴趣的:(#,SpringCloud)