微服务组件Zuul——自定义过滤器

 

一、 Zuul及ZuulFilter

Zuul是SpringCloud常用组件之一,Zuul提供了服务网关的功能,可以实现负载均衡、反向代理、动态路由、请求转发等功能。

Zuul大部分功能都是通过过滤器实现的,Zuul中定义了四种标准的过滤器类型,同时,还支持自定义过滤器。

这些过滤器的类型对应请求的典型生命周期,代表不同的执行时机策略:

pre: 在请求被路由之前调用

route:在路由请求时被调用

error:处理请求时发生错误时被调用

post:在route和error过滤器之后被调用

在Zuul中定义了抽象类ZuulFilter,要使用自定义过滤器其需要关注ZuulFilter的部分内容:

1. String filterType : 通过filterType指定过滤器类型(执行时机策略)

扫描二维码关注公众号,回复: 11121090 查看本文章

2. int filterOrder :通过filterOrder定义过滤器的执行优先级,数值越小优先级越高

3. boolean shouldFilter:是否执行过滤器,为true才会执行过滤器的核心run方法

4. Object run:过滤器定义业务逻辑和执行的方法

如源码:

二、前置准备,编写通用抽象基类

1.定义通用的抽象过滤器基类,包含是否执行过滤器的规则判断,run方法,成功或失败逻辑。

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;

/**
 * 通用的抽象过滤器类
 */
public abstract class AbstractZuulFilter extends ZuulFilter {

    //用于在过滤器之间传递消息,数据保存在每个请求的ThreadLocal中
    RequestContext context;

    private final static String NEXT = "next";

    @Override
    public boolean shouldFilter() {
        RequestContext currentContext = RequestContext.getCurrentContext();
        return (boolean) currentContext.getOrDefault(NEXT, true);
    }

    @Override
    public Object run() throws ZuulException {
        //初始化context
        context = RequestContext.getCurrentContext();
        return cRun();
    }

    protected abstract Object cRun();

    Object fail(int code, String msg) {
        context.set(NEXT, false);
        context.setSendZuulResponse(false);
        context.getResponse().setContentType("text/html;charset=UTF-8");
        context.setResponseBody(String.format("{\"result\":\"%s!\"}", msg));
        return null;
    }

    Object sucess() {
        context.set(NEXT, true);
        return null;
    }
}

 2.定义具备执行时机策略的抽象过滤器基类

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


/**
 * @Auther: jesses
 * @Description: pre类型抽象过滤器类
 */
public abstract class AbstractPreZuulFilter extends AbstractZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
}
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;

/**
 * @Auther: jesses
 * @Description: post类型抽象过滤器类
 */
public abstract class AbstractPostZuulFilter extends AbstractZuulFilter {

    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }
}

三、实现自定义的过滤器

前置工作做完了,已经可以实现自定义的过滤器了。

为使Spring容器能够扫描到此过滤器Bean,需对自定义过滤器标注Component注解。

1.自定义token校验过滤器类。
因是token校验过滤器,要在具体的功能服务响应之前执行。所以继承pre类型的抽象过滤器。

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Auther: jesses
 * @Description: 自定义实现了简单的token校验规则的token校验过滤器类
 */
@Slf4j
@Component
public class TokenFilter extends AbstractPreZuulFilter {

    @Override
    protected Object cRun() {
        HttpServletRequest request = context.getRequest();
        log.info(String.format("%s request to %s"),
                request.getMethod(), request.getRequestURI().toString());
        Object token = request.getParameter("token");
        if (null==token){
            log.error("error : token is empty");
            return fail(401,"error : token is empty");
        }

        return sucess();
    }

    /**
     * filterOrder值越小,过滤器优先级越高
     * @return
     */
    @Override
    public int filterOrder() {
        return 1;
    }
}

2. 自定义限流过滤器

import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * @Auther: jesses
 * @Description: 自定义限流过滤器
 */
@Slf4j
@Component
public class RateLimitFilter extends AbstractPreZuulFilter {

    //每秒可以获取到两个令牌
    RateLimiter rateLimiter=RateLimiter.create(2.0);

    @Override
    protected Object cRun() {
        HttpServletRequest request = context.getRequest();
        if (rateLimiter.tryAcquire()){
            log.info("get rate token sucess");
            return sucess();
        }else {
            log.error("rate limit:{}",request.getRequestURI());
            return fail(402,"error : rate limit");
        }
    }

    @Override
    public int filterOrder() {
        return 2;
    }
}

3. 自定义请求日志记录过滤器

需要记录请求处理持续时间。

定义一个pre类型的过滤器在请求进入时记录请求开始时间。

定义一个post类型的过滤器,设置过滤器的执行优先级仅比响应高,过滤器会在业务功能服务执行完后响应前一步执行,获取的当前时间就时请求结束时间。

通过计算就可以在日志过滤器类得到请求的处理持续时间等等。

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Auther: jesses
 * @Description: 请求预置过滤器,在请求进入网关时在contex中设置开始时间
 */
@Slf4j
@Component
public class PreRequestFilter extends AbstractPreZuulFilter {
    @Override
    protected Object cRun() {
        context.set("startTime",System.currentTimeMillis());
        return sucess();
    }

    @Override
    public int filterOrder() {
        return 0;
    }
}
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

/**
 * @Auther: jesses
 * @Description: 日志记录过滤器
 */
@Slf4j
@Component
public class AccessLogFilter extends AbstractPreZuulFilter {
    @Override
    protected Object cRun() {
        Long startTime = (Long) context.get("startTime");

        long lastedTime = System.currentTimeMillis() - startTime;

        log.info("uri:{},lastedTime:{}", context.getRequest().getRequestURI(), lastedTime);

        return sucess();
    }

    @Override
    public int filterOrder() {
        return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
    }
}

因Component注解将过滤器注入容器中,当请求进入zuul网关时,就会根据过滤器时机策略执行对应的过滤器了。

发布了25 篇原创文章 · 获赞 4 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_41570691/article/details/104633941