zuul網關Filter處理流程及異常處理
阿新 • • 發佈:2018-05-18
println 沒有 actor stat blog 一個地方 cli ram color
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中。
本文轉載自: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。
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的執行順序。 直接用一個簡單的示例來看看結果 第一個前置過濾器packagecom.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"映射的問題。 所以我再定義一個Controllerpackage 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處理流程及異常處理