1. 程式人生 > >Spring Cloud學習(四)Zuul過濾器詳解

Spring Cloud學習(四)Zuul過濾器詳解

轉載自:http://www.itmuch.com/spring-cloud/zuul/spring-cloud-zuul-filter/
    http://blog.didispace.com/spring-cloud-zuul-exception-2/
    http://blog.didispace.com/spring-cloud-zuul-exception-3/

  過濾器是Zuul的核心元件,本節來詳細討論Zuul的過濾器。

過濾器型別與請求生命週期

  Zuul大部分功能都是通過過濾器來實現的。Zuul中定義了四種標準過濾器型別,這些過濾器型別對應於請求的典型生命週期。
  (1) PRE:這種過濾器在請求被路由之前呼叫。可利用這種過濾器實現身份驗證、在叢集中選擇請求的微服務、記錄除錯資訊等。
  (2) ROUTING:這種過濾器將請求路由到微服務。這種過濾器用於構建傳送給微服務的請求,並使用Apache HttpClient或Netfilx Ribbon請求微服務。
  (3) POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應新增標準的HTTP Header、收集統計資訊和指標、將響應從微服務傳送給客戶端等。
  (4) ERROR:在其他階段發生錯誤時執行該過濾器。

  除了預設的過濾器型別,Zuul還允許我們建立自定義的過濾器型別。例如,可以定製一種STATIC型別的過濾器,直接在Zuul中生成響應,而不將請求轉發到後端的微服務。
  Zuul請求的生命週期如圖所示,該圖詳細描述了各種型別的過濾器的執行順序。

  com.netflix.zuul.http.ZuulServlet的service方法實現也可以看出,Zuul處理外部請求過程時,各個型別過濾器的執行邏輯:

try {
    preRoute();
} catch (ZuulException e) {
    error(e);
    postRoute();
    return;
}
try {
    route();
} catch (ZuulException e) {
    error(e);
    postRoute();
    return;
}
try {
    postRoute();
} catch (ZuulException e) {
    error(e);
    return;
}

  從程式碼中可以看到三個try-catch塊,它們依次分別代表了pre、route、post三個階段的過濾器呼叫,在catch的異常處理中都會被error型別的過濾器進行處理。error型別的過濾器處理完畢之後,除了來自post階段的異常之外,都會再被post過濾器進行處理。

自定義Zuul過濾器

  理解過濾器型別和請求生命週期後,來編寫一個Zuul過濾器。編寫Zuul的過濾器非常簡單,只需繼承抽象類ZuulFilter,然後實現幾個抽象方法就可以了。
  示例程式碼,定義一個簡單的Zuul過濾器,它實現了在請求被路由之前檢查HttpServletRequest中是否有accessToken引數,若有就進行路由,若沒有就拒絕訪問,返回401 Unauthorized錯誤。

@Component
public class AccessFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(AccessFilter.class);

    @Override
    public String filterType() {
        return "pre";
    }

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

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());

        Object accessToken = request.getParameter("accessToken");
        if (accessToken == null) {
            log.warn("access token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("miss token");
            return null;
        }
        log.info("access token ok");
        return null;
    }
}

  在上面實現的過濾器程式碼中,通過繼承ZuulFilter抽象類並重寫了下面的四個方法來實現自定義的過濾器。這四個方法分別定義了:

  • filterType:過濾器的型別,它決定過濾器在請求的哪個生命週期中執行。這裡定義為pre,代表會在請求被路由之前執行。詳細可以參考com.netflix.zuul.ZuulFilter.filterType() 中的註釋。
  • filterOrder:過濾器的執行順序。當請求在一個階段中存在多個過濾器時,需要根據該方法返回的值來依次執行。
  • shouldFilter:判斷該過濾器是否需要被執行。這裡直接返回了true,因此該過濾器對所有請求都會生效。實際運用中可以利用該函式來指定過濾器的有效範圍。
  • run:過濾器的具體邏輯。這裡通過ctx.setSendZuulResponse(false)令zuul過濾該請求,不對其進行路由。

Zuul預設過濾器

  Spring Cloud Zuul自帶的核心過濾器,例如DebugFilterFormBodyWrapperFilterPreDecorationFilter等。這些過濾器都存放在spring-cloud-netflix-core這個Jar包的org.springframework.cloud.netflix.zuul.filters包中。具體的各類過濾器可以參看如下圖:

禁用Zuul過濾器

  Spring Cloud預設為Zuul編寫並啟用了一些過濾器,一些場景下,想要禁用掉部分過濾器,此時該怎麼辦呢?只需設定zuul.<SimpleClassName>.<filterType>.disable=true,即可禁用SimpleClassName所對應的過濾器。以過濾器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter為例,只需設定zuul.SendResponseFilter.post.disable=true,即可禁用該過濾器。