微信公衆號:I am CR7
若有問題或建議,請在下方留言;
最近更新:2018-12-29javascript
繼上一篇Spring Cloud Netflix Zuul源碼分析之預熱篇,咱們知道了兩個重要的類:ZuulHandlerMapping和SimpleControllerHandlerAdapter。今天,ZuulHandlerMapping正式亮相,咱們來分析它是什麼時候註冊的路由信息。java
1server:
2 port: 8558
3
4zuul:
5 routes:
6 user-api:
7 path: /user-api/**
8 stripPrefix: true
9 url: http://localhost:9091/
10 role-api:
11 path: /role-api/**
12 stripPrefix: true
13 url: http://localhost:9092/
14 resource:
15 path: /resource/**
16 stripPrefix: true
17 url: http://testi.phoenixpay.com/
複製代碼
http://localhost:8558/role-api/info/7web
http請求經由tomcat容器,會進入到StandardWrapperValve類的invoke()方法裏。至於緣由,不是本文討論內容,後續會寫tomcat源碼分析作專門講解,這裏不作展開,請接着往下看。spring
1public final void invoke(Request request, Response response)
2 throws IOException, ServletException {
3 StandardWrapper wrapper = (StandardWrapper) getContainer();
4 Servlet servlet = null;
5 // 第一篇文章中講解的,DispatcherServlet初始化處理,返回DispatcherServlet實體
6 servlet = wrapper.allocate();
7 // Create the filter chain for this request
8 ApplicationFilterChain filterChain =
9 ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
10 // 調用ApplicationFilterChain的doFilter方法,處理請求
11 filterChain.doFilter(request.getRequest(),
12 response.getResponse());
13}
複製代碼
繼續走進去,看核心類ApplicationFilterChain的doFilter方法。後端
1@Override
2public void doFilter(ServletRequest request, ServletResponse response)
3 throws IOException, ServletException {
4
5 if( Globals.IS_SECURITY_ENABLED ) {
6 final ServletRequest req = request;
7 final ServletResponse res = response;
8 java.security.AccessController.doPrivileged(
9 new java.security.PrivilegedExceptionAction<Void>() {
10 @Override
11 public Void run()
12 throws ServletException, IOException {
13 internalDoFilter(req,res);
14 return null;
15 }
16 }
17 );
18 } else {
19 internalDoFilter(request,response);
20 }
21}
22
23private void internalDoFilter(ServletRequest request,
24 ServletResponse response)
25 throws IOException, ServletException {
26
27 // 遍歷過濾器鏈,依次執行過濾器的doFilter方法
28 if (pos < n) {
29 ApplicationFilterConfig filterConfig = filters[pos++];
30 Filter filter = filterConfig.getFilter();
31 filter.doFilter(request, response, this);
32 }
33 // 調用DispatcherServlet的service方法,正式處理請求
34 servlet.service(request, response);
35}
複製代碼
補充:ApplicationFilterChain的設計採用職責鏈模式,包含了一組過濾器,逐個遍歷執行doFilter方法,最後在執行結束前會回調回ApplicationFilterChain。這組過濾器裏WebMvcMetricsFilter是咱們本文討論的重點,至於其餘過濾器您能夠自行查看。對於DispatcherServlet的service方法會在下一篇文章中進行詳細講解。api
該過濾器繼承OncePerRequestFilter,是用來統計HTTP請求在通過SpringMVC處理後的時長和結果。doFilter()方法在父類中,具體邏輯由子類覆蓋doFilterInternal方法去處理。咱們來看下doFilterInternal()源碼。tomcat
1@Override
2protected void doFilterInternal(HttpServletRequest request,
3 HttpServletResponse response, FilterChain filterChain)
4 throws ServletException, IOException {
5 filterAndRecordMetrics(request, response, filterChain);
6}
7
8private void filterAndRecordMetrics(HttpServletRequest request,
9 HttpServletResponse response, FilterChain filterChain)
10 throws IOException, ServletException {
11 Object handler;
12 try {
13 handler = getHandler(request);
14 }
15 catch (Exception ex) {
16 logger.debug("Unable to time request", ex);
17 filterChain.doFilter(request, response);
18 return;
19 }
20 filterAndRecordMetrics(request, response, filterChain, handler);
21}
22
23private Object getHandler(HttpServletRequest request) throws Exception {
24 HttpServletRequest wrapper = new UnmodifiableAttributesRequestWrapper(request);
25 // 從ApplicationContext裏獲取HandlerMappingIntrospector實體
26 for (HandlerMapping mapping : getMappingIntrospector().getHandlerMappings()) {
27 HandlerExecutionChain chain = mapping.getHandler(wrapper);
28 if (chain != null) {
29 if (mapping instanceof MatchableHandlerMapping) {
30 return chain.getHandler();
31 }
32 return null;
33 }
34 }
35 return null;
36}
複製代碼
這裏根據HandlerMappingIntrospector獲取全部的HandlerMapping,逐個遍歷,獲取請求匹配的Handler,封裝進HandlerExecutionChain,交由WebMvcMetricsFilter進行相關的統計處理。這一系列HandlerMapping裏就包含了咱們要看的ZuulHandlerMapping。咱們繼續往裏看。微信
ZuulHandlerMapping做爲MVC HandlerMapping的實現,用來將進入的請求映射到遠端服務。爲了便於理解其getHandler()原理,筆者畫了一個類圖。mvc
調用ZuulHandlerMapping的getHandler(),最終會進入lookupHandler(),這是本文分析的重點,往下看源碼。app
1public class ZuulHandlerMapping extends AbstractUrlHandlerMapping {
2
3 private final RouteLocator routeLocator;
4 private final ZuulController zuul;
5 private ErrorController errorController;
6 private PathMatcher pathMatcher = new AntPathMatcher();
7 private volatile boolean dirty = true;
8
9 @Override
10 protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
11 if (this.errorController != null && urlPath.equals(this.errorController.getErrorPath())) {
12 return null;
13 }
14 if (isIgnoredPath(urlPath, this.routeLocator.getIgnoredPaths())) return null;
15 RequestContext ctx = RequestContext.getCurrentContext();
16 if (ctx.containsKey("forward.to")) {
17 return null;
18 }
19 if (this.dirty) {
20 synchronized (this) {
21 if (this.dirty) {
22 // 首次會註冊路由信息
23 registerHandlers();
24 this.dirty = false;
25 }
26 }
27 }
28 //調用父類方法根據urlPath查找Handler
29 return super.lookupHandler(urlPath, request);
30 }
31
32 private void registerHandlers() {
33 Collection<Route> routes = this.routeLocator.getRoutes();
34 if (routes.isEmpty()) {
35 this.logger.warn("No routes found from RouteLocator");
36 }
37 else {
38 // 遍歷路由信息,將urlPath和ZuulController註冊到父類handlerMap裏
39 for (Route route : routes) {
40 registerHandler(route.getFullPath(), this.zuul);
41 }
42 }
43 }
複製代碼
如此一來,請求http://localhost:8558/role-api/info/7,就會由AbstractUrlHandlerMapping的lookupHandler方法,找到ZuulController。雖然WebMvcMetricsFilter對找到的ZuulController只是作統計相關的處理,可是這爲後面講述DispatcherServlet正式處理請求,由Zuul轉發到後端微服務,打下了很好的基礎。
12018-12-28 14:32:11.939 [http-nio-8558-exec-9] DEBUG o.s.c.n.z.filters.SimpleRouteLocator - route matched=ZuulRoute{id='user-api', path='/user-api/**', serviceId='null', url='http://localhost:9091/', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, }
22018-12-28 14:32:11.940 [http-nio-8558-exec-9] DEBUG o.s.c.n.z.filters.SimpleRouteLocator - route matched=ZuulRoute{id='role-api', path='/role-api/**', serviceId='null', url='http://localhost:9092/', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, }
32018-12-28 14:32:11.940 [http-nio-8558-exec-9] DEBUG o.s.c.n.z.filters.SimpleRouteLocator - route matched=ZuulRoute{id='resource', path='/resource/**', serviceId='null', url='http://testi.phoenixpay.com/', stripPrefix=true, retryable=null, sensitiveHeaders=[], customSensitiveHeaders=false, }
4
52018-12-28 14:36:27.630 [http-nio-8558-exec-9] INFO o.s.c.n.zuul.web.ZuulHandlerMapping - Mapped URL path [/user-api/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
62018-12-28 14:36:38.358 [http-nio-8558-exec-9] INFO o.s.c.n.zuul.web.ZuulHandlerMapping - Mapped URL path [/role-api/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
72018-12-28 14:36:44.478 [http-nio-8558-exec-9] INFO o.s.c.n.zuul.web.ZuulHandlerMapping - Mapped URL path [/resource/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
8
92018-12-28 14:38:36.556 [http-nio-8558-exec-9] DEBUG o.s.c.n.zuul.web.ZuulHandlerMapping - Matching patterns for request [/role-api/info/7] are [/role-api/**]
102018-12-28 14:39:11.325 [http-nio-8558-exec-9] DEBUG o.s.c.n.zuul.web.ZuulHandlerMapping - URI Template variables for request [/role-api/info/7] are {}
112018-12-28 14:39:56.557 [http-nio-8558-exec-9] DEBUG o.s.c.n.zuul.web.ZuulHandlerMapping - Mapping [/role-api/info/7] to HandlerExecutionChain with handler [org.springframework.cloud.netflix.zuul.web.ZuulController@399337] and 1 interceptor
複製代碼
讀到這裏,您可能會說,又被騙了,標題黨,仍是沒有提到Zuul。我只想說,"宅陰陰"(泰國旅遊時學到的惟一一句)。凡事都有個按部就班的過程,本文依舊是在爲後面Zuul分析作鋪墊,一口吃一個胖子,每每容易消化不良。但願筆者的良苦用心,您可以明白,固然更但願對您能有所幫助。最後,感謝您的支持!!!祝進步,2018年12月29日,祁琛。