SpringCloud系列:源碼解析Spring Cloud Zuul

      網友投稿 901 2025-03-31

      Zuul?架構圖


      在zuul中,整個請求的過程是這樣的,首先將請求給zuulservlet處理,zuulservlet中有一個zuulRunner對象,該對象中初始化了RequestContext:作為存儲整個請求的一些數據,并被所有的zuulfilter共享。zuulRunner中還有FilterProcessor,FilterProcessor作為執行所有的zuulfilter的管理器。FilterProcessor從filterloader?中獲取zuulfilter,而zuulfilter是被filterFileManager所加載,并支持groovy熱加載,采用了輪詢的方式熱加載。有了這些filter之后,zuulservelet首先執行的Pre類型的過濾器,再執行route類型的過濾器,最后執行的是post?類型的過濾器,如果在執行這些過濾器有錯誤的時候則會執行error類型的過濾器。執行完這些過濾器,最終將請求的結果返回給客戶端。

      zuul工作原理源碼分析

      在之前已經講過,如何使用zuul,其中不可缺少的一個步驟就是在程序的啟動類加上@EnableZuulProxy,該EnableZuulProxy類代碼如下:

      其中,引用了ZuulProxyConfiguration,跟蹤ZuulProxyConfiguration,該類注入了DiscoveryClient、RibbonCommandFactoryConfiguration用作負載均衡相關的。注入了一些列的filters,比如PreDecorationFilter、RibbonRoutingFilter、SimpleHostRoutingFilter,代碼如如下:

      它的父類ZuulConfiguration,引用了一些相關的配置。在缺失zuulServlet bean的情況下注入了ZuulServlet,該類是zuul的核心類。

      同時也注入了其他的過濾器,比如ServletDetectionFilter、DebugFilter、Servlet30WrapperFilter,這些過濾器都是pre類型的。

      它也注入了post類型的,比如SendResponseFilter,error類型,比如SendErrorFilter,route類型比如SendForwardFilter,代碼如下:

      初始化ZuulFilterInitializer類,將所有的filter?向FilterRegistry注冊。

      而FilterRegistry管理了一個ConcurrentHashMap,用作存儲過濾器的,并有一些基本的CURD過濾器的方法,代碼如下:

      FilterLoader類持有FilterRegistry,FilterFileManager類持有FilterLoader,所以最終是由FilterFileManager注入filterFilterRegistry的ConcurrentHashMap的。FilterFileManager到開啟了輪詢機制,定時的去加載過濾器,代碼如下:

      Zuulservlet作為類似于Spring MVC中的DispatchServlet,起到了前端控制器的作用,所有的請求都由它接管。它的核心代碼如下:

      跟蹤init(),可以發現這個方法為每個請求生成了RequestContext,RequestContext繼承了ConcurrentHashMap,在請求結束時銷毀掉該RequestContext,RequestContext的生命周期為請求到zuulServlet開始處理,直到請求結束返回結果。RequestContext類在存儲了很多重要的信息,包括HttpServletRequest、HttpServletRespons、ResponseDataStream、ResponseStatusCode等。RequestContext對象在處理請求的過程中,一直存在,所以這個對象為所有Filter共享。

      從ZuulServlet的service()方法可知,它是先處理pre()類型的處理器,然后在處理route()類型的處理器,最后再處理post類型的處理器。

      首先來看一看pre()的處理過程,它會進入到ZuulRunner,該類的作用是將請求的HttpServletRequest、HttpServletRespons放在RequestContext類中,并包裝了一個FilterProcessor,代碼如下:

      而FilterProcessor類為調用filters的類,比如調用pre類型所有的過濾器:

      跟蹤runFilters()方法,可以發現,它最終調用了FilterLoader的getFiltersByType(sType)方法來獲取同一類的過濾器,然后用for循環遍歷所有的ZuulFilter,執行了processZuulFilter()方法,跟蹤該方法可以發現最終是執行了ZuulFilter的方法,最終返回了該方法返回的Object對象。

      route、post類型的過濾器的執行過程和pre執行過程類似。

      Zuul默認過濾器

      Zuul默認注入的過濾器,它們的執行順序在FilterConstants類,我們可以先定位在這個類,然后再看這個類的過濾器的執行順序以及相關的注釋,可以很輕松定位到相關的過濾器,也可以直接打開spring-cloud-netflix-core.jar的zuul.filters包,可以看到一些列的filter,現在我以表格的形式,列出默認注入的filter.

      過濾器

      order

      描述

      類型

      ServletDetectionFilter

      -3

      檢測請求是用DispatcherServlet還是ZuulServlet

      pre

      Servlet30WrapperFilter

      -2

      在Servlet 3.0?下,包裝requests

      pre

      FormBodyWrapperFilter

      -1

      解析表單數據

      pre

      SendErrorFilter

      0

      如果中途出現錯誤

      error

      DebugFilter

      1

      設置請求過程是否開啟debug

      pre

      PreDecorationFilter

      5

      根據uri決定調用哪一個route過濾器

      pre

      RibbonRoutingFilter

      10

      如果寫配置的時候用ServiceId則用這個route過濾器,該過濾器可以用Ribbon做負載均衡,用hystrix做熔斷

      route

      SimpleHostRoutingFilter

      100

      如果寫配置的時候用url則用這個route過濾

      route

      SendForwardFilter

      500

      用RequestDispatcher請求轉發

      route

      SendResponseFilter

      1000

      用RequestDispatcher請求轉發

      post

      過濾器的order值越小,就越先執行,并且在執行過濾器的過程中,它們共享了一個RequestContext對象,該對象的生命周期貫穿于請求,可以看出優先執行了pre類型的過濾器,并將執行后的結果放在RequestContext中,供后續的filter使用,比如在執行PreDecorationFilter的時候,決定使用哪一個route,它的結果的是放在RequestContext對象中,后續會執行所有的route的過濾器,如果不滿足條件就不執行該過

      濾器的run方法。最終達到了就執行一個route過濾器的run()方法。

      而error類型的過濾器,是在程序發生異常的時候執行的。

      post類型的過濾,在默認的情況下,只注入了SendResponseFilter,該類型的過濾器是將最終的請求結果以流的形式輸出給客戶單。

      SpringCloud系列:源碼解析Spring Cloud Zuul

      進入到SimpleHostRoutingFilter類的方法的run()方法,核心代碼如下:

      查閱這個類的全部代碼可知,該類創建了一個HttpClient作為請求類,并重構了url,請求到了具體的服務,得到的一個CloseableHttpResponse對象,并將CloseableHttpResponse對象的保存到RequestContext對象中。并調用了ProxyRequestHelper的setResponse方法,將請求狀態碼,流等信息保存在RequestContext對象中。

      這個過濾器的order為1000,在默認且正常的情況下,是最后一個執行的過濾器,該過濾器是最終將得到的數據返回給客戶端的請求。

      在它的run()方法里,有兩個方法:addResponseHeaders()和writeResponse(),即添加響應頭和寫入響應數據流。

      其中writeResponse()方法是通過從RequestContext中獲取ResponseBody獲或者ResponseDataStream來寫入到HttpServletResponse中的,但是在默認的情況下ResponseBody為null,而ResponseDataStream在route類型過濾器中已經設置進去了。具體代碼如下:

      如何在zuul上做日志處理

      由于zuul作為api網關,所有的請求都經過這里,所以在網關上,可以做請求相關的日志處理。我的需求是這樣的,需要記錄請求的url,ip地址,參數,請求發生的時間,整個請求的耗時,請求的響應狀態,甚至請求響應的結果等。很顯然,需要實現這樣的一個功能,需要寫一個ZuulFliter,它應該是在請求發送給客戶端之前做處理,并且在route過濾器路由之后,在默認的情況下,這個過濾器的order應該為500-1000之間。那么如何獲取這些我需要的日志信息呢?找RequestContext,在請求的生命周期里這個對象里,存儲了整個請求的所有信息。

      現在編碼,在代碼的注釋中,做了詳細的說明,代碼如下:

      現在讀者也許有疑問,如何得到的statrtTime,即請求開始的時間,其實這需要另外一個過濾器,在網絡請求route之前(大部分耗時都在route這一步),在過濾器中,在RequestContext存儲一個時間即可,另寫一個過濾器,代碼如下:

      可能還有這樣的需求,我需要將響應結果,也要存儲在log中,在之前已經分析了,在route結束后,將從具體服務獲取的響應流存儲在RequestContext中,在SendResponseFilter過濾器寫入在HttpServletResponse中,最終返回給客戶端。那么我只需要在SendResponseFilter寫入響應流之前把響應流寫入到log日志中即可,那么會引發另外一個問題,因為響應流寫入到log后,RequestContext就沒有響應流了,在SendResponseFilter就沒有流輸入到HttpServletResponse中,導致客戶端沒有任何的返回數據,那么解決的辦法是這樣的:

      InputStream inputStream =RequestContext.getCurrentContext().getResponseDataStream();

      InputStream newInputStream= copy(inputStream);

      transerferTolog(inputStream);RequestContext.getCurrentContext().setResponseDataStream(newInputStream);

      從RequestContext獲取到流之后,首先將流copy一份,將流轉化下字符串,存在日志中,再set到RequestContext中,這樣SendResponseFilter就可以將響應返回給客戶端。這樣的做法有點影響性能,如果不是字符流,可能需要做更多的處理工作。

      方志朋簡介:SpringCloud中國社區聯合創始人,博客訪問量突破一千萬,愛好開源,熱愛分享,活躍于各大社區,保持著非常強的學習驅動力,終身學習踐行者,終身學習受益者。目前就職于國內某家知名互聯網保險公司,擔任DEVOPS工程師,對微服務領域和持續集成領域研究較深,精通微服務框架SpringCloud

      Spring Cloud Spring

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:牙科統計報表模板范文圖片(牙科日報表格式)
      下一篇:Excel中進行表格內文字換行的操作方法(excel表格里的文字自動換行的操作教程(2))
      相關文章
      亚洲国产成人综合| 亚洲国产av美女网站| 亚洲熟女乱色一区二区三区| 亚洲码在线中文在线观看| 亚洲国产精品免费视频| 亚洲AV日韩AV永久无码久久 | 自拍偷自拍亚洲精品被多人伦好爽| 国产亚洲视频在线观看网址| 亚洲a无码综合a国产av中文| 亚洲1区2区3区精华液| 久久精品国产亚洲av瑜伽| 亚洲av永久中文无码精品综合| 亚洲熟女综合色一区二区三区| 亚洲精品无码久久久久YW| 亚洲AV无码AV日韩AV网站| 大胆亚洲人体视频| 亚洲国产91精品无码专区| 久久久久久久亚洲精品| 亚洲中文字幕久久精品无码喷水| 国产亚洲日韩在线三区| 亚洲精品乱码久久久久久中文字幕| 亚洲精品无码永久在线观看你懂的| 久久91亚洲人成电影网站| 久久亚洲精品成人777大小说| 亚洲丁香色婷婷综合欲色啪| 中文字幕亚洲免费无线观看日本| 91精品国产亚洲爽啪在线观看| 亚洲美女色在线欧洲美女| 亚洲一区二区三区高清视频| 色婷五月综激情亚洲综合| 亚洲色在线无码国产精品不卡| 亚洲成a∨人片在无码2023| 一本色道久久88亚洲综合 | 久久亚洲精品成人AV| 亚洲欧洲中文日产| 亚洲伊人久久大香线蕉AV| 精品亚洲国产成人av| 国产亚洲人成A在线V网站| 亚洲国产精品VA在线看黑人| 亚洲黄网站wwwwww| 国产v亚洲v天堂a无|