本文基於Spring Cloud Edgware.SR6,Zuul版本1.3.1,解析Zuul的請求攔截機制,讓你們對Zuul的原理有個大概的認識和了解。若有不對的地方,歡迎指正。spring
spring boot啓動過程當中,一系列spring管理的bean會被初始化,其中包括ZuulController,它經過繼承ServletWrappingController來初始化ZuulServlet瀏覽器
spring boot啓動完成後,經過瀏覽器發起網關請求,請求會到達DispatcherServlet.doDispatch(),此方法會查找符合的Handler和HandlerAdapter來處理請求。咱們來看下它是如何找到zuul的handler。app
this.handlerMappings中包含了當前應用全部繼承HandlerMapping接口的實現類,經過遍歷它來查找符合當前request請求的HandlerExecutionChain
進來發現調用的是AbstractHandlerMapping.getHandler(),內部先調用AbstractUrlHandlerMapping.getHandlerInternal(),查詢匹配的handler,若是沒有,則使用默認的handler,而後包裝成HandlerExecutionChain返回。ui
AbstractUrlHandlerMapping.getHandlerInternal()方法內部調用了lookupHandler()。this
進來發現是ZuulHandlerMapping重寫的lookupHandler()。該方法首先判斷是否有異常,沒有的話再判斷是不是忽略的請求,不是的話就註冊handlers,而後調用父類的url
lookupHandler()方法返回。
咱們看下registerHandlers()作了什麼。this.routeLocator.getRoutes()就是獲取註冊在eureka的服務列表,而後遍歷,
依次保存到AbstractUrlHandlerMapping.handlerMap中
再來看下super.lookupHandler(urlPath, request)。spa
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // 這裏就從上面註冊好的handlerMap中獲取請求urlPath對應的handler Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // 若是獲取不到,則進行正則匹配,若是還匹配不到的話,則返回null List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } else if (useTrailingSlashMatch()) { if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) { matchingPatterns.add(registeredPattern +"/"); } } } String bestMatch = null;// 匹配到以後,用請求urlPath對應的patternComparator,對全部匹配的url進行排序,以後獲取第一個匹配的url Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath); if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, patternComparator); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestMatch = matchingPatterns.get(0); } if (bestMatch != null) {// 先根據排序後的第一個url獲取對應的handler,若是沒有的話則用」/」再取一次handler = this.handlerMap.get(bestMatch); if (handler == null) { if (bestMatch.endsWith("/")) { handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1)); } if (handler == null) { throw new IllegalStateException( "Could not find handler for best pattern match [" + bestMatch + "]"); } } // 若是handler是String,則從應用上下文中獲取對應的bean if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request);// 解析映射url的後半段請求uriString pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath); // 最後再確認一次bestMatch是不是最匹配請求的路由 Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars); uriTemplateVariables.putAll(decodedVars); } } if (logger.isDebugEnabled()) { logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables); }// 構建HandlerExecutionChain並返回return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
至此,終於找到了zuul的handler,其中有些細節沒有提或是略過,有興趣的朋友能夠自行下去翻閱。debug
總結一下:3d
1.請求執行到DispatcherServlet.doDispatch(),此方法中調用getHandler(),遍歷全部實現handlerMapping接口的實現類來查找請求對應的HandlerExecutionChaincode
2.getHandler()內部是遍歷執行AbstractHandlerMapping.getHandler(),它的內部又是執行的AbstractUrlHandlerMapping.getHandlerInternal(),而AbstractUrlHandlerMapping內部調用的lookupHandler()實則是ZuulHandlerMapping重寫的lookupHandler(),目的是獲取註冊中心的消費者路由列表,
3.而後ZuulHandlerMapping調用父類AbstractUrlHandlerMapping的lookupHandler(),用請求url匹配路由列表,獲取最匹配的一個路由,包裝成HandlerExecutionChain返回