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


本文轉載自: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"方法里返回你想給客戶端返回的值即可。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM