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

在上一期的SpringCloud解析之Zuul(一),我们了解了spring boot在接收一个网关请求后,是如何找到与之匹配的ZuulHandlerMapping。今天我们继续探寻,这个ZuulHandlerMapping是如何处理网关请求的。

在开始之前,我们先了解一下,为什么spring boot要如此大费周章的,只为找到ZuulHandlerMapping。

通过上一期文章,我们知道所有的浏览器请求都会到达DispatcherServlet,并且DispatcherServlet中有一个成员变量handlerMappings,会扫描所有继承HandlerMapping接口的实现类,其实它里面还有一个成员变量handlerAdapters,这个变量会扫描所有HandlerAdapter接口的实现类。从名字命名上可以看出,handlerAdapters包含了所有的处理器适配器,所有的HandlerMapping都会通过其对应的HandlerAdapter来执行,目的是为了解耦。因为这样设计之后,DispatcherServlet和HandlerMapping都不会包含特定的逻辑代码,也不需要关心是什么类型的请求,只需要调用对应的HandlerAdapter的方法就可以了。

ZuulHandlerMapping里面有什么呢?ZuulHandlerMapping中有一个成员变量ZuulController,ZuulController继承了ServletWrappingController,ServletWrappingController中有servletClass和servletInstance。

在上一期开头,我粗略的提及,在spring boot启动过程中会初始化ZuulController,继而通过反射初始化ZuulServlet。详细的说,是spring boot启动过程中会通过org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration自动配置类来初始化zuul,其中包括ZuulHandlerMapping。所以,ZuulHandlerMapping中有ZuulController,ZuulController中有ZuulServlet,这些对象实例在spring boot启动成功后就有了。

好,在了解了这些之后,我们继续今天的内容。DispatcherServlet.doDispatch()在获取到ZuulHandlerMapping之后,马上调用方法getHandlerAdapter()获取对应的HandlerAdapter。

可以看到,getHandlerAdapter()内部是在遍历handlerAdapters,然后通过调用各自的supports()方法判断哪一个handlerAdapter支持前面获取的handler处理器。这里取到的是SimpleControllerHandlerAdapter。

取到handlerAdapter之后,开始调用handlerAdapter的handle()方法,mappedHandler.getHandler()就是ZuulController。SimpleControllerHandlerAdapter.handle()内部,是ZuulController.handleRequest(),其内部又是调用父类的handleRequestInternal(),真正起作用的是servletInstance.service(),而servletInstance正是ZuulServlet(我们开头讲到的)

下面我们看下,ZuulServlet.service()做了什么。

public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
// 初始化,将servletRequest和servletResponse存储到请求上下文RequestContext init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse); // Marks this request as having passed through the "Zuul engine", as opposed to servlets
// explicitly bound in web.xml, for which requests will not have the same data attached
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
// 顺序执行ZuulFilter过滤器,也包括我们继承ZuulFilter的自定义过滤器
try {
preRoute();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
} } catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}

简直一目了然。我们往下看,它是如何会执行我们继承ZuulFilter的自定义过滤器。(上文忘了说,在ZuulServlet实例化后,会调用init()方法初始化ZuulRunner)。

以preRoute()为例,不难看出,最终的执行者是FilterProcessor,在runFilters()方法中,获取对应类型的List<ZuulFilter>,然后遍历执行processZuulFilter(zuulFilter)

FilterLoader.getInstance().getFiltersByType(sType)内容如下,就是从hashFiltersByType中获取filterType对应的List<ZuulFilter>

那么新问题就来了,hashFiltersByType里的值怎么来的。FilterLoader还有一个putFilter(File file)方法,通过查找源码很容易发现,这个方法是在FilterFileManager中被调用的。官方的解释是,FilterFileManager类负责初始化和更新filter(但是我一直搞不懂它是如何调用运行的,因为我打断点都没有效果,猜测是Groovy,不甚了解,知道的小伙伴帮忙评论解释下,感激不尽)。

INSTANCE.manageFiles()最终调用的就是FilterLoader.putFilter()方法,INSTANCE.startPoller()则是启动一个守护线程,轮询执行manageFiles()。

回过头来,我们继续看processZuulFilter(zuulFilter)干了什么。额,其实关键就下面一句,

ZuulFilter.runFilter()方法,首先判断!isFilterDisabled(),shouldFilter(),同时满足时才执行run()方法,即配置文件中isFilter没有禁用,并且自定义过滤器的shouldFilter()返回true,才会执行重写ZuulFilter的run()方法。

至此,zuul网关执行过滤器和自定义过滤器的过程就讲完了(啊,累死了)。

最后,总结一下:

1.spring boot通过适配器设计模式,使用对应的HandlerAdapter来执行ZuulHandlerMapping,以达到解耦的目的。而ZuulHandlerMapping最终是调用的ZuulServlet.service()

2.ZuulServlet.service()里面,也是取出filterType类型的所有ZuulFilter,然后遍历执行ZuulFilter的run()方法,这也是为什么我们写自定义过滤器要继承ZuulFilter的原因。

下一期,我们来聊聊Zuul对请求过滤处理后,是如何将请求转发到对应的服务器的。

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

  1. SpringCloud解析之Zuul(一)

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

  2. SpringCloud学习之zuul

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

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

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

  4. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  5. 数据结构图文解析之:二叉堆详解及C++模板实现

    0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...

  6. sql执行计划解析案例(二)

    sql执行计划解析案例(二)   今天是2013-10-09,本来以前自己在专注oracle sga中buffer cache 以及shared pool知识点的研究.但是在研究cache buffe ...

  7. asp.net 生成、解析条形码和二维码

    原文 asp.net 生成.解析条形码和二维码 一.条形码 一维码,俗称条形码,广泛的用于电子工业等行业.比如我们常见的书籍背面就会有条形码,通过扫描枪等设备扫描就可以获得书籍的ISBN(Intern ...

  8. spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关)

    spring cloud: zuul(二): zuul的serviceId/service-id配置(微网关) zuul: routes: #路由配置表示 myroute1: #路由名一 path: ...

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

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

随机推荐

  1. uwp开发————换背景图片

    原文:uwp开发----换背景图片 用后台代码来实现对容器背景的切换,用本地图片作为背景. 把需要的图片素材放到Assets文件夹下 前台xaml代码如下: <Grid x:Name=" ...

  2. 淘宝开源Key/Value结构数据存储系统Tair技术剖析

    摘要: Tair的功能 Tair是一个Key/Value结构数据的解决方案,它默认支持基于内存和文件的两种存储方式,分别和我们通常所说的缓存和持久化存储对应. Tair除了普通Key/Value系统提 ...

  3. MySQL 主从配置 读写分离

    Master配置 1.创建用户: 在Master MySQL上创建一个用户‘repl’,并允许其他Slave服务器可以通过远程访问Master,通过该用户读取二进制日志,实现数据同步. create ...

  4. 冒泡排序C#实现,使用委托,包括三种方式:Fun<>,匿名方法,Lambda表达式

    冒泡排序是一种简单的排序方法,适合于小量数字排序,对于大量数字(超过10个),还有更高效的排序方法. 这里的实现的冒泡排序,需实现功能: 不仅数字排序,还要对任意对象排序 示例: 对People对象的 ...

  5. 很幽默的讲解六种Socket IO模型 Delphi版本(自己Select查看,WM_SOCKET消息通知,WSAEventSelect自动收取,Overlapped I/O 事件通知模型,Overlapped I/O 完成例程模型,IOCP模型机器人)

    很幽默的讲解六种Socket IO模型(转)本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教. 一:select模型 二:WSAAsyncSel ...

  6. c# 查询本机可用的代理ip

    现在有很多网站都提供免费的代理ip,但是你会发现很多网站显示的可以用的 ,在自己电脑上是用不了,写个小代码提取出自己电脑上可以用的代理,没什么技术含量,只是为了记录一下 string strUrl = ...

  7. 桌面程序阻止Windows关机(使用Message.Result取得DefWindowProc API函数的返回值,非常重要)

    Windows Client 客户端在关机,不外乎两种情况: 1. 没有处理 Windows 关机消息: 2.处理了关机消息,但是超时了: 上面这两种情况,都会让Windows 关不了机.在现实生活中 ...

  8. 使用pjax实现类似github无刷新更改页面url

    pjax=pushState+ajax,相信用过github的同学都知道,github部分页面采用了pjax这个项目来实现ajax无刷新加载的同时改变页面url.一起来学习一下这个插件吧. 我们都知道 ...

  9. 如何使用jQuery可以让滚轮滚到底部可以自动加载所需内容

    话不多说先上代码 $(window).scroll(function() {   var scrollTop = $(this).scrollTop(); //滚动高度  var windowHeig ...

  10. CI框架注意

    1.在ci框架中,如果想继承自己写的控制器,从而不继承CI_Controller,可以在application/core中定义控制器,从而就可以在controllers中的控制器继承. 2.在ci框架 ...