做設(shè)計網(wǎng)站賺錢嗎百度風云排行榜
Spring Cloud Zuul 底層是基于Servlet實現(xiàn)的,核心是通過一系列的ZuulFilter來完成請求的轉(zhuǎn)發(fā)。
1、核心組件注冊
1.1. EnableZuulProxy注解
啟用Zuul作為微服務(wù)網(wǎng)關(guān),需要在Application應(yīng)用類加上@EnableZuulProxy注解,而該注解核心是利用@Import注解往Spring容器導(dǎo)入了ZuulProxyConfiguration配置類
1.2. ZuulProxyConfiguration
ZuulProxyConfiguration繼承了ZuulConfiguration。
1.2.1. ZuulConfiguration:
ZuulConfuguration主要是利用@Import往Spring容器注入了ServerPropertiesAutoConfiguration配置類(下一小節(jié)介紹),并且作為配置類往Spring容器注入了CompositeRouteLocator、SimpleRouteLocator、ZuulController、ZuulHandlerMapping、ZuulServlet等組件,基于Spring DispatcherServlet實現(xiàn)請求轉(zhuǎn)發(fā)入口。
還有ServletDetectionFilter、Servlet30WrapperFilter、SendResponseFilter、SendErrorFilter、SendForwardFilter等pre、post類型的過濾器,是Zuul實現(xiàn)路由轉(zhuǎn)發(fā)的核心過濾器。
還有ZuulRefreshListener監(jiān)聽器,同于監(jiān)聽應(yīng)用內(nèi)部事件,設(shè)置路由信息狀態(tài)為dirty,實現(xiàn)動態(tài)更新。
1.2.2. ZuulProxyConfiguration
當然了,ZuulProxyConfiguration本身也注入了實現(xiàn)路由轉(zhuǎn)發(fā)的核心過濾器,包含route類型的過濾器:RibbonRoutingFilter、SimpleHostRoutingFilter。
還有路由定位器DiscoveryClientRouteLocator,先調(diào)用父類SimpleRouteLocator獲取配置文件中的路由配置,然后再從注冊中心中補充路由信息。
還有一個非常重要的Listener:ZuulDiscoveryRefreshListener,它實現(xiàn)了ApplicationListener接口,主要監(jiān)聽InstanceRegisteredEvent、ParentHearbeatEvent和HeartbeatEvent,根據(jù)注冊中心發(fā)送的事件來更新最新的路由信息(設(shè)置路由信息狀態(tài)為dirty)。
2、路由配置注冊
上面已經(jīng)提到,Zuul是基于Servlet實現(xiàn)的,而根據(jù)請求URL找到對應(yīng)Handler是利用HandlerMapping完成的,而Zuul也根據(jù)此實現(xiàn)了ZuulHandlerMapping實現(xiàn)類。
2.1. ZuulHandlerMapping
DispatcherServlet#initHandlerMappings
2.2. ZuulHandlerMapping#lookupHandler
在DispatcherServlet在首次請求分發(fā)時,就會遍歷所有HandlerMapping,然后根據(jù)請求去獲取對應(yīng)的Handler(HandlerExecutionChain,包含Handler和攔截器),當遍歷到ZuulHandlerMapping時,會調(diào)用lookupHandler方法,如果是首次調(diào)用,會觸發(fā)上面的registerHandlers方法,進行路由配置注冊。
2.3. ZuulHandlerMapping#registerHandlers
ZuulHandlerMapping首次根據(jù)url查找Handler時,會先找到所有的路由配置,然后遍歷注冊Handler(ZuulController);這里查找所有路由配置就是上面提到的DiscoveryClientRouteLocator。
3、請求處理
DispatcherServlet分發(fā)請求的流程:
圖片拿自網(wǎng)絡(luò)
3.1. ZuulController
在2.3.中,ZuulHandlerMapping給路由配置注冊Handler時,對應(yīng)的Handler是ZuulController。ZuulController繼承了ServletWrappingController,底層是實現(xiàn)Controller接口。
3.2. SimpleControllerHandlerAdapter
根據(jù)上面流程圖,找到HandlerMapping后,會繼續(xù)找到能執(zhí)行對應(yīng)Handler的HandlerAdapter;而上面也提到,ZuulController是實現(xiàn)于Controller接口,所以最后定位到的是SimpleControllerHandlerAdapter。
SimpleControllerAdapter執(zhí)行請求邏輯非常簡單,就是執(zhí)行Handler的handleRequest方法,即執(zhí)行ZuulController的handleRequest方法。
3.3. ZuulController#handleRequest
ZuulController的handleRequest很簡單,調(diào)用的是父類的handleRequestInternal方法。
但是我們需要注意ZuulController的構(gòu)造函數(shù),里面給servletClass、servletName和supportedMethods賦值了,其中servletClass尤為關(guān)鍵,因為后續(xù)處理就是調(diào)用此類實例的方法。
3.4. ServletWrappingController
ServletWrappingController重寫了InitializingBean#afterPropertiesSet方法,在設(shè)置實例屬性后,根據(jù)servletClass實例化了servletInstance對象,這里就是ZuulServlet的實例。
ServletWrappingController的handleRequestInternal方法也很簡單,就是調(diào)用servletInstance的service方法,這里就是ZuulServlet#service方法。
3.5. ZuulServlet#service
ZuulServlet的service方法邏輯很簡單,都是利用ZuulRunner來完成的;在ServletWrappingController實例化servletInstance時,同時調(diào)用了servletInstance的init方法,此時ZuulServlet同時會創(chuàng)建一個ZuulRunner實例。
service方法邏輯:
- 執(zhí)行ZuulRunner#init方法,創(chuàng)建請求上下文RequestContext,并將利用HttpServletRequestWrapper和HttpServletResponseWrapper分別將HttpServletRequest和HttpServletResponse包裝起來。
- 調(diào)用ZuulRunner#preRoute方法執(zhí)行前置過濾器
- 調(diào)用ZuulRunner#route方法執(zhí)行路由過濾器
- 調(diào)用ZuulRunner#postRoute方法執(zhí)行后置過濾器
- 如果步驟2到步驟4出現(xiàn)錯誤,則執(zhí)行ZuulRunner的error方法
- 最后,清理RequestContext內(nèi)容(ThreadLocal)
3.6. ZuulRunner
ZuulRunner實現(xiàn)也是非常簡單,底層是利用FilterProcessor來實現(xiàn)的。
3.7. FilterProcessor
FilterProcessor執(zhí)行過濾器的邏輯也非常簡單,就是根據(jù)過濾器類型找到所有的過濾器,然后遍歷調(diào)用processZuulFilter方法執(zhí)行,里面只要是執(zhí)行ZuulFilter的runFilter方法,并且對錯誤信息和成功信息做統(tǒng)計。
3.8. FilterLoader和FilterRegistry
FilterProcessor中是利用FilterLoader來完成過濾器的加載的,而FilterLoader最終是利用FilterRegistry來完成過濾器的加載。
FilterLoader和FilterRegistry都是單例,在ZuulFilterConfiguration中創(chuàng)建,并注入到ZuulFilterInitializer中,最后并將ZuulFilterInitializer注入到Spring容器中。
ZuulFilterInitializer實現(xiàn)了ServletContextListener接口,在Spring容器完成初始化時,會將ZuulFilter集合注入到FilterRregistry中。
4. 核心過濾器
這里只要分析核心過濾器,不包含所有的過濾器。
4.1. 前置過濾器
4.1.1. ServletDetectionFilter
執(zhí)行順序為-3,主要是區(qū)分請求是通過Spring的DispatcherServlet處理運行的還是ZuulServlet來處理運行的。
4.1.2. Servlet30WrapperFilter
執(zhí)行順序為-2,主要是將HttpServletRequest包裝成Servlet30RequestWrapper。
4.1.3. FormBodyWrapperFilter
執(zhí)行順序為-1,條件要么是Context-Type為application/x-www-form-urlencoded的請求,要么是Context-Type為multipart/form-data,且是由String的DispatcherServlet處理的請求,主要是將HttpServletRequest包裝成FormBodyRequestWrapper。
4.1.4. DebugFilter
執(zhí)行順序為1,條件要么配置里指定zuul.debug.request為true,要么請求參數(shù)debug為true。主要用來將當前請求上下文中的debugRouting和debugRequest參數(shù)設(shè)置為true;主要是做到靈活開關(guān)debug模式,開啟debug模式時,會打印一些日志方便分析問題。
4.1.5. PreDecorationFilter
執(zhí)行順序為5,條件要求請求上下文中不存在forward.do和serviceId參數(shù),主要是做一個預(yù)處理,將相關(guān)信息存到上下文中,包含路由、后置、錯誤過濾器的過濾條件判斷信息。
4.2. 路由過濾器
4.2.1. RibbonRoutingFilter
執(zhí)行順序為10,條件是請求上下文中routeHost為null并且serviceId不為null,主要是構(gòu)建Ribbon命令上下文,并且發(fā)起請求轉(zhuǎn)發(fā)。
在發(fā)起請求轉(zhuǎn)發(fā)的時候,需要構(gòu)建HTTP客戶端,這里會根據(jù)配置和依賴來選用指定的HTTP客戶端。
4.2.2. SimpleHostRoutingFilter
執(zhí)行順序為100,條件是請求上下文中routeHost不為null,主要是直接根據(jù)物理地址發(fā)送請求,這里是直接調(diào)用原生的HttpClient包的客戶端。
4.2.3. SendForwardFilter
執(zhí)行順序為500,條件是請求上下文中forward.do不為null并且sendForwardFilter.ran為false,主要是做本地轉(zhuǎn)發(fā)。
4.3. 后置過濾器
4.3.1. SendResponseFilter
執(zhí)行順序為1000,條件是請求上下文中異常為null,并且響應(yīng)頭或響應(yīng)體不為null,主要是將響應(yīng)寫回給客戶端。
4.4. 錯誤過濾器
4.4.1 SendErrorFilter
執(zhí)行順序為0,條件是請求上下文中異常不為null,并且sendErrorFilter.ran為false,主要是將異常寫回給客戶端。