javascript
SpringCloud学习系列之七 ----- Zuul路由网关的过滤器和异常处理
前言
在上篇中介紹了SpringCloud Zuul路由網關的基本使用版本,本篇則介紹基于SpringCloud(基于SpringBoot2.x,.SpringCloud Finchley版)中的路由網關的過濾器Filter以及異常處理的教程。
SpringCloud Zuul Filter
介紹
過濾器概述
Zuul的中心是一系列過濾器,能夠在HTTP請求和響應的路由過程中執行一系列操作。
以下是Zuul過濾器的主要特征:
- 類型:通常在應用過濾器時在路由流程中定義階段(盡管它可以是任何自定義字符串)
- 執行順序:在類型中應用,定義跨多個過濾器的執行順序
- 標準:執行過濾器所需的條件
- 操作:滿足條件時要執行的操作
- Zuul提供了一個動態讀取,編譯和運行這些過濾器的框架。過濾器不直接相互通信 - 而是通過RequestContext共享狀態,RequestContext對每個請求都是唯一的。
過濾器目前用Groovy編寫,盡管Zuul支持任何基于JVM的語言。每個Filter的源代碼都寫入Zuul服務器上的一組指定目錄,這些目錄會定期輪詢更改。更新的過濾器從磁盤讀取,動態編譯到正在運行的服務器中,并由Zuul為每個后續請求調用。
過濾器類型與請求生命周期
Zuul大部分功能都是通過過濾器來實現的。Zuul中定義了四種標準過濾器類型,這些過濾器類型對應于請求的典型生命周期。
- PRE:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實現身份驗證、在集群中選擇請求的微服務、記錄調試信息等。
- ROUTING:這種過濾器將請求路由到微服務。這種過濾器用于構建發送給微服務的請求,并使用Apache HttpClient或Netfilx Ribbon請求微服務。
- POST:這種過濾器在路由到微服務以后執行。這種過濾器可用來為響應添加標準的HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
- ERROR:在其他階段發生錯誤時執行該過濾器。
官網Wiki 提供的四種過濾器的生命周期圖。
注:此段來之Netflix / zuul的官網Wiki,地址:https://github.com/Netflix/zuul/wiki/How-it-Works。
開發準備
開發環境
- JDK:1.8
- SpringBoot:2.0.6.RELEASE
- SpringCloud:Finchley.SR2
注:不一定非要用上述的版本,可以根據情況進行相應的調整。需要注意的是SpringBoot2.x以后,jdk的版本必須是1.8以上!
服務端
由于在上一篇中我們已經完成了Zuul路由網關的基本功能實現,所以服務端這塊我們可以直接把之前的項目拿來直接使用,然后更改相應的名稱以及相關代碼即可。
自定義過濾器
這里我們來編寫自定義一個Filter實現類,看看該類是如何工作的。
在編寫該類的時候,發現自定義的Filter類需要繼承ZuulFilter這個類,我們查看該類的源碼,發現了該類定義了兩個抽象的方法,并且該類實現了IZuulFilter該接口,該接口也定義了兩個方法,我們就來看看這幾個方法到底是干嘛的吧。
ZuulFilter源碼:
filterType這個方法表示按類型對過濾器進行分類,分別是pre、post、route和error,在FilterConstants這個常量類中已經進行定義了,其意義在上述的Filter請求的典型生命周期已經進行過說明了。
filterOrder 這個方法表示Filter執行的順序,數值越小優先級越高。
IZuulFilter
shouldFilter該方法表示是否執行該過濾器,也可以說是該過濾器的一個開關。
run過濾器的具體邏輯。在該函數中,我們可以實現自定義的過濾邏輯,來確定是否要攔截當前的請求,不對其進行后續的路由,或是在請求路由返回結果之后,對處理結果做一些加工等。
看完上述的源碼之后,這里我們再來編寫自定的Filter代碼。
首先繼承ZuulFilter該類,然后實現里面的方法。
首先是shouldFilter方法,這里我們就直接返回true;
然后是filterType,這里我們就設置為前置過濾器,在請求被路由之前調用。
繼而是filterOrder,這里我們就設置0;
最后是run,這是過濾器的核心業務代碼,這里我們就簡單一點,獲取請求的url,如果該url攜帶了token,我們就讓他通過,否則直接攔截。
當然,我們需要將該過濾類使用Bean注解使其生效。
那么代碼如下:
自定義的Filter代碼:
@Component public class MyZuulFilter extends ZuulFilter{@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();HttpServletRequest request = ctx.getRequest();ctx.addZuulResponseHeader("Content-type", "text/json;charset=UTF-8");ctx.getResponse().setCharacterEncoding("UTF-8");System.out.println("請求地址:"+request.getRequestURI());String token = request.getParameter("token");String msg="請求成功!";if(token==null) {ctx.setSendZuulResponse(false);msg="請求失敗!";ctx.setResponseBody(msg);ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());} return msg;}@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}@Overridepublic int filterOrder() {return 0;}@Beanpublic MyZuulFilter zuulFilter() {return new MyZuulFilter();} }自定義異常類處理
Zuul除了可以自定義過濾器之外,也可以對異常結果進行處理,以保持返回值一致。在進行Zuul使用的時候發現了在發生了異常之后,會調用SendErrorFilter異常過濾器,對異常經常處理,同時重定向至/error這個路徑中。所以如果我們需要自定義對異常處理的話,繼承SendErrorFilter該類就可以實現了。我們查看SendErrorFilter源碼,其實也是繼承ZuulFilter該類并實現里面的一些方法,做的自定義異常封裝,其實也可以把SendErrorFilter該類當做一個自定義的過濾器。
由于SendErrorFilter是對ZuulFilter類進行了二次封裝,所以我們自定義的Error代碼只需繼承SendErrorFilter改成,然后實現其中的run方法即可。
自定義的Error代碼:
@Component public class MyErrorFilter extends SendErrorFilter{@Overridepublic Object run() {String msg="請求失敗!"; try{RequestContext ctx = RequestContext.getCurrentContext();ExceptionHolder exception = findZuulException(ctx.getThrowable());System.out.println("錯誤信息:"+exception.getErrorCause());msg+="error:"+exception.getErrorCause();HttpServletResponse response = ctx.getResponse();response.setCharacterEncoding("UTF-8");response.getOutputStream().write(msg.getBytes()); }catch (Exception ex) {ex.printStackTrace();ReflectionUtils.rethrowRuntimeException(ex);}return msg;}@Beanpublic MyErrorFilter errorFilter() {return new MyErrorFilter();} }這里我們還需要禁用SendErrorFilter過濾器,不然是不會使用我們自定的異常過濾器的。
在application.properties 添加如下配置:
zuul.SendErrorFilter.error.disable=true這里順便說下禁用過濾器的規則。組件實現的過濾器,滿足執行條件時都是會執行的,若我們想禁用某個過濾器時,可以在配置文件中配置。
規則:
說明:
SimpleClassName為類名,filterType過濾器類型
當然,如果覺得上述的異常處理還是不夠優雅的話,可以使用ControllerAdvice注解進行全局異常處理,該注解的使用示例可以從個人的springboot項目中進行找到,地址:https://github.com/xuwujing/springBoot-study
自定義異常回退處理
在之前的關于springcloud中SpringCloud學習系列之三----- 斷路器(Hystrix)和斷路器監控(Dashboard)這篇文章中講解過服務的降級處理,其實這里的處理也是類似,也就是某個服務無法進行訪問的時候,進行回退處理。
這里我們自定義異常回退處理的代碼相對而已也比較簡單,只需實現FallbackProvider該接口的方法既可。
該類的源碼如下:
getRoute該方法主要是指定需要回退服務的名稱。
fallbackResponse該方法提供基于執行失敗原因并進行回退響應。
了解之后該源碼之后,我們再來編寫一個自定義異常回退處理的類。
自定義的Fallback代碼:
@Component public class MyFallback implements FallbackProvider {private static final String SERVER_NAME="springcloud-zuul-filter-server2";@Overridepublic String getRoute() {return SERVER_NAME;}@Overridepublic ClientHttpResponse fallbackResponse(String route, Throwable cause) {//標記不同的異常為不同的http狀態值if (cause instanceof HystrixTimeoutException) {return response(HttpStatus.GATEWAY_TIMEOUT);} else {//可繼續添加自定義異常類return response(HttpStatus.INTERNAL_SERVER_ERROR);}}//處理private ClientHttpResponse response(final HttpStatus status) {String msg="該"+SERVER_NAME+"服務暫時不可用!";return new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() throws IOException {return status;}@Overridepublic int getRawStatusCode() throws IOException {return status.value();}@Overridepublic String getStatusText() throws IOException {return status.getReasonPhrase();}@Overridepublic void close() {}@Overridepublic InputStream getBody() throws IOException {return new ByteArrayInputStream(msg.getBytes());}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}};}@Beanpublic MyFallback eurekaClientFallback() {return new MyFallback();} }客戶端
客戶端這邊,我們可以把之前springcloud-zuul項目中的springcloud-zuul-server1和springcloud-zuul-server2拿來進行使用既可。
測試
完成上述的代碼開發后,我們來進行springcloud-zuul的一系列自定義過濾測試。
首先依次啟動springcloud-zuul-filter-eureka、springcloud-zuul-filter-gateway、springcloud-zuul-filter-server1和springcloud-zuul-filter-server2這四個項目。其中9009是服務端springcloud-zuul-filter-gatewayr的端口,9010是第一個客戶端springcloud-zuul-filter-server1的端口,9011是第二個客戶端springcloud-zuul-filter-server2的端口。
這里順便說下路由網關的默認規則:http://ZUUL_HOST:ZUUL_PORT/微服務實例名(serverId)/** ,轉發至serviceId對應的微服務。比如在瀏覽器輸入:http://localhost:9009/springcloud-zuul-filter-server1/hello地址, 它就會跳轉訪問到:http://localhost:9010/hello/這個地址上。使用這個方式進行測試可以幫助我們更好的了解本篇文章的實現目的。
自定義過濾器功能測試
完成上述的項目啟動成功之后。
我們首先在瀏覽器上輸入:
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm
界面返回:
請求失敗!
這里看到直接進行攔截了,并返回了相應的信息、
加上token之后在進行訪問
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm&token=123
界面返回:
pancm,Hello World!
我們按照我們自定的規則進行訪問之后,發現可以直接訪問到我們想要訪問的服務上,因此該次測試也符合我們的預期,達成了自定義過濾器的處理。
自定義異常類處理功能測試
上述的功能測試ok之后,這里我們停止掉springcloud-zuul-filter-server1服務,然后在瀏覽器上輸入:
http://localhost:9009/springcloud-zuul-filter-server1/hello?name=pancm&token=123
界面返回:
請求失敗!error:GENERAL請求失敗!error:GENERAL
注: 這里實際是調用了兩次。
可以看到這次測試也符合我們的預期,達成了自定義異常的處理。
自定義異常回退處理功能測試
這里我們再來停止掉springcloud-zuul-filter-server2服務,然后在瀏覽器上輸入:
http://localhost:9009/springcloud-zuul-filter-server2/hi?name=pancm&token=123
界面返回:
該springcloud-zuul-filter-server2服務暫時不可用!
可以看到這次測試也符合我們的預期,達成了 自定義異常回退處理的處理。這里也順便說下,自定義該服務的異常和自定義異常回退處理最好不要在同一個服務同時使用,如果同時使用,會優先進行自定義異常回退處理的處理。
其他
參考:
https://github.com/Netflix/zuul/wiki/How-it-Works
https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_router_and_filter_zuul
http://www.itmuch.com/spring-cloud/zuul/spring-cloud-zuul-filter/
https://blog.lqdev.cn/2018/10/17/SpringCloud/chapter-ten/#參考資料
項目地址
基于SpringBoot2.x、SpringCloud的Finchley版本開發的地址:https://github.com/xuwujing/springcloud-study
如果感覺項目不錯,希望能給個star,謝謝!
springcloud系列博客:
SpringCloud學習系列之一 ----- 搭建一個高可用的注冊中心(Eureka)
SpringCloud學習系列之二 ----- 服務消費者(Feign)和負載均衡(Ribbon)
SpringCloud學習系列之三----- 斷路器(Hystrix)和斷路器監控(Dashboard)
SpringCloud學習系列之四-----配置中心(Config)使用詳解)
SpringCloud學習系列之五-----配置中心(Config)和消息總線(Bus)完美使用版)
SpringCloud學習系列之六 ----- 路由網關Zuul基礎使用教程)
音樂推薦
原創不易,如果感覺不錯,希望留言推薦!您的支持是我寫作的最大動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm
個人博客出處:http://www.panchengming.com
轉載于:https://www.cnblogs.com/xuwujing/p/10888517.html
總結
以上是生活随笔為你收集整理的SpringCloud学习系列之七 ----- Zuul路由网关的过滤器和异常处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何优化limit
- 下一篇: 很甜很撩暧昧的情话留言短句251个