• SpringCloud解析之Zuul(一)


    本文基于Spring Cloud Edgware.SR6,Zuul版本1.3.1,解析Zuul的请求拦截机制,让大家对Zuul的原理有个大概的认识和了解。如有不对的地方,欢迎指正。

    spring boot启动过程中,一系列spring管理的bean会被初始化,其中包括ZuulController,它通过继承ServletWrappingController来初始化ZuulServlet

    image_thumb7

    image_thumb4

    image_thumb6

    spring boot启动完成后,通过浏览器发起网关请求,请求会到达DispatcherServlet.doDispatch(),此方法会查找符合的Handler和HandlerAdapter来处理请求。我们来看下它是如何找到zuul的handler。

    image_thumb8

    this.handlerMappings中包含了当前应用所有继承HandlerMapping接口的实现类,通过遍历它来查找符合当前request请求的HandlerExecutionChain

    image_thumb9

    进来发现调用的是AbstractHandlerMapping.getHandler(),内部先调用AbstractUrlHandlerMapping.getHandlerInternal(),查询匹配的handler,如果没有,则使用默认的handler,然后包装成HandlerExecutionChain返回。

    image_thumb11

    image_thumb10

    AbstractUrlHandlerMapping.getHandlerInternal()方法内部调用了lookupHandler()。

    image_thumb12

    image_thumb13

    进来发现是ZuulHandlerMapping重写的lookupHandler()。该方法首先判断是否有异常,没有的话再判断是否是忽略的请求,不是的话就注册handlers,然后调用父类的

    lookupHandler()方法返回。

    image_thumb15

    image_thumb14

    我们看下registerHandlers()做了什么。this.routeLocator.getRoutes()就是获取注册在eureka的服务列表,然后遍历,
    依次保存到AbstractUrlHandlerMapping.handlerMap中

    image_thumb16

    再来看下super.lookupHandler(urlPath, request)。

    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的后半段请求uri
    		String 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,其中有些细节没有提或是略过,有兴趣的朋友可以自行下去翻阅。

    总结一下:

    1.请求执行到DispatcherServlet.doDispatch(),此方法中调用getHandler(),遍历所有实现handlerMapping接口的实现类来查找请求对应的HandlerExecutionChain

    2.getHandler()内部是遍历执行AbstractHandlerMapping.getHandler(),它的内部又是执行的AbstractUrlHandlerMapping.getHandlerInternal(),而AbstractUrlHandlerMapping内部调用的lookupHandler()实则是ZuulHandlerMapping重写的lookupHandler(),目的是获取注册中心的消费者路由列表,

    3.然后ZuulHandlerMapping调用父类AbstractUrlHandlerMapping的lookupHandler(),用请求url匹配路由列表,获取最匹配的一个路由,包装成HandlerExecutionChain返回

    作者:wangl110
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    微信支付 h5
    微信支付 h5
    Android stadio butternife工具
    Android stadio butternife工具
    Android stadio 自定义debug release keystore
    Android stadio 自定义debug release keystore
    Android 微信支付步骤
    Android 微信支付步骤
    t
    t
  • 原文地址:https://www.cnblogs.com/wangl110/p/11191998.html
Copyright © 2020-2023  润新知