微服務組件Zuul——自定義過濾器

 

一、 Zuul及ZuulFilter

Zuul是SpringCloud常用組件之一,Zuul提供了服務網關的功能,可以實現負載均衡、反向代理、動態路由、請求轉發等功能。

Zuul大部分功能都是通過過濾器實現的,Zuul中定義了四種標準的過濾器類型,同時,還支持自定義過濾器。

 

這些過濾器的類型對應請求的典型生命週期,代表不同的執行時機策略:

pre: 在請求被路由之前調用

route:在路由請求時被調用

error:處理請求時發生錯誤時被調用

post:在route和error過濾器之後被調用

 

在Zuul中定義了抽象類ZuulFilter,要使用自定義過濾器其需要關注ZuulFilter的部分內容:

1. String filterType : 通過filterType指定過濾器類型(執行時機策略)

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網關時,就會根據過濾器時機策略執行對應的過濾器了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章