1. 程式人生 > >zuul網關Filter處理流程及異常處理

zuul網關Filter處理流程及異常處理

println 沒有 actor stat blog 一個地方 cli ram color

本文轉載自:https://blog.csdn.net/tianyaleixiaowu/article/details/77893822

上一篇介紹了java網關Zuul的簡單使用,進行請求路由轉發和過濾器的基本操作。

這一篇主要看一下它的過濾器Filter的工作流程及異常處理。

首先看到Filter的四個方法,FilterType,filterOrder,shouldFilter,run。

filterType代表過濾類型

PRE: 該類型的filters在Request routing到源web-service之前執行。用來實現Authentication、選擇源服務地址等
ROUTING:該類型的filters用於把Request routing到源web-service,源web-service是實現業務邏輯的服務。這裏使用HttpClient請求web-service。
POST:該類型的filters在ROUTING返回Response後執行。用來實現對Response結果進行修改,收集統計數據以及把Response傳輸會客戶端。
ERROR:上面三個過程中任何一個出現錯誤都交由ERROR類型的filters進行處理。 主要關註 pre、post和error。分別代表前置過濾,後置過濾和異常過濾。 如果你的filter是pre的,像上一篇那種,就是指請求先進入pre的filter類,你可以進行一些權限認證,日誌記錄,或者額外給Request增加一些屬性供後續的filter使用。pre會優先按照order從小到大執行,然後再去執行請求轉發到業務服務。 再說post,如果type為post,那麽就會執行完被路由的業務服務後,再進入post的filter,在post的filter裏,一般做一些日誌記錄,或者額外增加response屬性什麽的。 最後error,如果在上面的任何一個地方出現了異常,就會進入到type為error的filter中。

filterOrder代表過濾器順序

這個不多說,試一下就知道了。

shouldFilter代表這個過濾器是否生效

true代表生效,false代表不生效。那麽什麽情況下使用不生效呢,不生效幹嘛還要寫這個filter類呢? 其實是有用的,有時我們會動態的決定讓不讓一個filter生效,譬如我們可能根據Request裏是否攜帶某個參數來判斷是否需要生效,或者我們需要從上一個filter裏接收某個數據來決定,再或者我們希望能手工控制是否生效(使用如Appolo之類的配置中心,來動態設置該字段)。

Run方法

這個是主要的處理邏輯的地方,我們做權限控制、日誌等都是在這裏。 下圖是filter的執行順序。 技術分享圖片 直接用一個簡單的示例來看看結果 第一個前置過濾器
package
com.tianyalei.testzuul; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @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() { //是否執行該過濾器,true代表需要過濾 return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString()); //獲取傳來的參數accessToken Object accessToken = request.getParameter("accessToken"); if(accessToken == null) { log.warn("access token is empty"); //過濾該請求,不往下級服務去轉發請求,到此結束 ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); ctx.setResponseBody("{\"result\":\"accessToken為空!\"}"); ctx.getResponse().setContentType("text/html;charset=UTF-8"); return null; } //如果有token,則進行路由轉發 log.info("access token ok"); //這裏return的值沒有意義,zuul框架沒有使用該返回值 return null; } }

第二個前置過濾器

package com.tianyalei.testzuul;  
  
import com.netflix.zuul.ZuulFilter;  
import com.netflix.zuul.context.RequestContext;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  
  
import javax.servlet.http.HttpServletRequest;  
  
@Component  
public class SecondFilter extends ZuulFilter {  
  
    private static Logger log = LoggerFactory.getLogger(SecondFilter.class);  
  
    @Override  
    public String filterType() {  
        //前置過濾器  
        return "pre";  
    }  
  
    @Override  
    public int filterOrder() {  
        //優先級,數字越大,優先級越低  
        return 1;  
    }  
  
    @Override  
    public boolean shouldFilter() {  
        //是否執行該過濾器,true代表需要過濾  
        return true;  
    }  
  
    @Override  
    public Object run() {  
        RequestContext ctx = RequestContext.getCurrentContext();  
        HttpServletRequest request = ctx.getRequest();  
  
        log.info("second過濾器");  
  
        return null;  
  
    }  
  
}

後置過濾器

package com.tianyalei.testzuul;  
  
import com.netflix.zuul.ZuulFilter;  
import com.netflix.zuul.context.RequestContext;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  
  
@Component  
public class PostFilter extends ZuulFilter {  
  
    private static Logger log = LoggerFactory.getLogger(PostFilter.class);  
  
    @Override  
    public String filterType() {  
        //後置過濾器  
        return "post";  
    }  
  
    @Override  
    public int filterOrder() {  
        //優先級,數字越大,優先級越低  
        return 0;  
    }  
  
    @Override  
    public boolean shouldFilter() {  
        //是否執行該過濾器,true代表需要過濾  
        return true;  
    }  
  
    @Override  
    public Object run() {  
        RequestContext ctx = RequestContext.getCurrentContext();  
        log.info("進入post過濾器");  
        System.out.println(ctx.getResponseBody());  
  
        ctx.setResponseBody("post後置數據");  
  
        int i = 1 / 0;  
  
        return null;  
  
    }  
  
}

異常過濾器

package com.tianyalei.testzuul;  
  
import com.netflix.zuul.ZuulFilter;  
import com.netflix.zuul.context.RequestContext;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  
  
@Component  
public class ErrorFilter extends ZuulFilter {  
  
    private static Logger log = LoggerFactory.getLogger(ErrorFilter.class);  
  
    @Override  
    public String filterType() {  
        //異常過濾器  
        return "error";  
    }  
  
    @Override  
    public int filterOrder() {  
        //優先級,數字越大,優先級越低  
        return 0;  
    }  
  
    @Override  
    public boolean shouldFilter() {  
        //是否執行該過濾器,true代表需要過濾  
        return true;  
    }  
  
    @Override  
    public Object run() {  
        RequestContext ctx = RequestContext.getCurrentContext();  
  
        log.info("進入異常過濾器");  
  
        System.out.println(ctx.getResponseBody());  
  
        ctx.setResponseBody("出現異常");  
  
        return null;  
  
    }  
  
}

定義好之後,直接測試看看

技術分享圖片技術分享圖片技術分享圖片

可以看到結果就是按照上面說的順序在執行。 但是最終給用戶呈現這樣一個界面就不合適的,我們應該去處理這個"/error"映射的問題。 所以我再定義一個Controller
package com.tianyalei.testzuul;  
  
import org.springframework.boot.autoconfigure.web.ErrorController;  
import org.springframework.web.bind.annotation.RequestMapping;  
@RestController  
public class ErrorHandlerController implements ErrorController {  
  
    /** 
     * 出異常後進入該方法,交由下面的方法處理 
     */  
    @Override  
    public String getErrorPath() {  
        return "/error";  
    }  
  
    @RequestMapping("/error")  
    public String error() {  
        return "出現異常";  
    }  
}  

在"/error"方法裏返回你想給客戶端返回的值即可。

zuul網關Filter處理流程及異常處理