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

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

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

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

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

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

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

  1. lookupHandler()方法返回。

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

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

  1. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
  2. // 这里就从上面注册好的handlerMap中获取请求urlPath对应的handler
  3. Object handler = this.handlerMap.get(urlPath);
  4. if (handler != null) {
  5. // Bean name or resolved handler?
  6. if (handler instanceof String) {
  7. String handlerName = (String) handler;
  8. handler = getApplicationContext().getBean(handlerName);
  9. }
  10. validateHandler(handler, request);
  11. return buildPathExposingHandler(handler, urlPath, urlPath, null);
  12. }
  13.  
  14. // 如果获取不到,则进行正则匹配,如果还匹配不到的话,则返回null
  15. List<String> matchingPatterns = new ArrayList<String>();
  16. for (String registeredPattern : this.handlerMap.keySet()) {
  17. if (getPathMatcher().match(registeredPattern, urlPath)) {
  18. matchingPatterns.add(registeredPattern);
  19. }
  20. else if (useTrailingSlashMatch()) {
  21. if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
  22. matchingPatterns.add(registeredPattern +"/");
  23. }
  24. }
  25. }
  26.  
  27. String bestMatch = null;
  1. // 匹配到之后,用请求urlPath对应的patternComparator,对所有匹配的url进行排序,之后获取第一个匹配的url
  2. Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
  3. if (!matchingPatterns.isEmpty()) {
  4. Collections.sort(matchingPatterns, patternComparator);
  5. if (logger.isDebugEnabled()) {
  6. logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
  7. }
  8. bestMatch = matchingPatterns.get(0);
  9. }
  10. if (bestMatch != null) {
  1. // 先根据排序后的第一个url获取对应的handler,如果没有的话则用”/”再取一次
  1. handler = this.handlerMap.get(bestMatch);
  2. if (handler == null) {
  3. if (bestMatch.endsWith("/")) {
  4. handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
  5. }
  6. if (handler == null) {
  7. throw new IllegalStateException(
  8. "Could not find handler for best pattern match [" + bestMatch + "]");
  9. }
  10. }
  11. // 如果handler是String,则从应用上下文中获取对应的bean
  12. if (handler instanceof String) {
  13. String handlerName = (String) handler;
  14. handler = getApplicationContext().getBean(handlerName);
  15. }
  16. validateHandler(handler, request);
  1. // 解析映射url的后半段请求uri
  1. String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
  2.  
  3. // 最后再确认一次bestMatch是否是最匹配请求的路由
  4. Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
  5. for (String matchingPattern : matchingPatterns) {
  6. if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
  7. Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
  8. Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
  9. uriTemplateVariables.putAll(decodedVars);
  10. }
  11. }
  12. if (logger.isDebugEnabled()) {
  13. logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
  14. }
  1. // 构建HandlerExecutionChain并返回
  1. return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
  2. }
  3.  
  4. // No handler found...
  5. return null;
  6. }

至此,终于找到了zuul的handler,其中有些细节没有提或是略过,有兴趣的朋友可以自行下去翻阅。

总结一下:

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

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

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

SpringCloud解析之Zuul(一)的更多相关文章

  1. SpringCloud解析之Zuul(二)

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

  2. SpringCloud学习之zuul

    一.为什么要有网关 我们先看一个图,如果按照consumer and server(最初的调用方式),如下所示 这样我们要面临如下问题: 1. 用户面临着一对N的问题既用户必须知道每个服务.随着服务的 ...

  3. springcloud 实战 网关zuul使用中遇到的相关问题

    springcloud 实战  网关zuul使用中遇到的相关问题 1.网关zuul使用时,跨域问题在网关中配置pre过滤器: response.setHeader("Access-Contr ...

  4. 基于SpringCloud搭建项目-Zuul篇(六)

    本文主要介绍zuul的基本原理和在sprngcloud服务下如何使用 一.简单介绍 Zuul 是 Netflix OSS 中的一员,是一个基于 JVM 路由和服务端的负载均衡器.提供路由.监控.弹性. ...

  5. SpringCloud学习之Zuul统一异常处理及回退

    一.Filter中统一异常处理 其实在SpringCloud的Edgware SR2版本中对于ZuulFilter中的错误有统一的处理,但是在实际开发当中对于错误的响应方式,我想每个团队都有自己的处理 ...

  6. SpringCloud之网关 Zuul(四)

    一 Zuul简介 zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用. Zuul 在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架.Z ...

  7. SpringCloud 进阶之Zuul(路由网关)

    1. Zuul(路由网关) Zuul 包含了对请求的路由和过滤两个最主要的功能; 路由功能:负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础; 过滤功能:负责对请求的处理过程进行干 ...

  8. springcloud微服务总结 zuul

    一 springcloud网关组件理解: 为什么需要网关呢? 我们知道我们要进入一个服务本身,很明显我们没有特别好的办法,直接输入IP地址+端口号,我们知道这样的做法很糟糕的,这样的做法大有问题,首先 ...

  9. SpringCloud路由网关Zuul

    一.什么是网关 Zuul的主要功能是路由转发和过滤器.路由功能是微服务的一部分,比如/api/user转发到到user服务,/api/shop转发到到shop服务.zuul默认和Ribbon结合实现了 ...

随机推荐

  1. WPF Calendar 日历控件 样式自定义

    原文:WPF Calendar 日历控件 样式自定义 粗略的在代码上做了些注释 blend 生成出来的模版 有的时候 会生成 跟 vs ui界面不兼容的代码 会导致可视化设计界面 报错崩溃掉 但是确不 ...

  2. 【C#】解决MouseHook捕获鼠标动作,在有些电脑上SetWindowsHookEx失败返回0的问题

    原文:[C#]解决MouseHook捕获鼠标动作,在有些电脑上SetWindowsHookEx失败返回0的问题 最近在debug鼠标位置捕获的功能时发现在其中的一台开发电脑上,SetWindowsHo ...

  3. ArcGIS for Desktop入门教程_第八章_Desktop学习资源 - ArcGIS知乎-新一代ArcGIS问答社区

    原文:ArcGIS for Desktop入门教程_第八章_Desktop学习资源 - ArcGIS知乎-新一代ArcGIS问答社区 1 学习资源 用户在学习和应用过程中,可以参考的资源如下: 1. ...

  4. 获取其他进程的命令行(ReadProcessMemory其它进程的PPROCESS_PARAMETERS和PEB结构体)

    type   UNICODE_STRING = packed record     Length: Word;     MaximumLength: Word;     Buffer: PWideCh ...

  5. .NET错误:未找到类型或命名空间名称

    现象:编译项目时提示未找到类型或命名空间名称"... " 解决方法:如果是未找到类型,检查是否引用了类型所在的命名空间,使用using指令:如果是未找到命名空间,那么检查是否引用了 ...

  6. abp(net core)+easyui+efcore仓储系统——领域层创建实体(三)

    abp(net core)+easyui+efcore仓储系统目录 abp(net core)+easyui+efcore仓储系统——ABP总体介绍(一) abp(net core)+easyui+e ...

  7. 仿写confirm和alert弹框

    在工作中,我们常常会遇到原生的样式感觉比较丑,又和我们做的项目风格不搭.于是就有了仿写原生一些组件的念头,今天我就带大家仿写一下confirm和alert样式都可以自己修改. 有些的不好的地方请指出来 ...

  8. Android WebView设置背景透明

    Adndroid 2.x的设置 在Android 2.x下,设置webview背景为透明的方法: wvContent.setBackgroundColor(0); Adndroid 4.0 由于硬件加 ...

  9. 我所理解的Vue——学习心得体会1(Vue对象)

    初学Vue,总结如下: 1.首先要区分html的dom和js的dom 2.html的dom是View的范畴,js的dom是Model的范畴. 3.vue这库就是创建了伟大的new Vue()对象,把h ...

  10. Spring Cloud Stream整合RabbitMQ

    简介 Spring Cloud Stream是一个构建消息驱动微服务的框架,应用程序通过input(相当于consumer).output(相当于producer)来与Spring Cloud Str ...