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