本文基于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. 项目集成dubbo

    dubbo 用户指南: http://dubbo.io/User+Guide-zh.htm 开发指南:http://dubbo.io/Developer+Guide-zh.htm#DeveloperG ...

  2. linux oracle 启动全过程

    一:启动oracle [root@ccoracle ~]# su -l oracle [oracle@ccoracle ~]$ sqlplus /nolog SQL*Plus: Release 10. ...

  3. GO方法与接口

    Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承.虚函数.构造函数和析构函数.隐藏的this指针等. 方法 Go 语言中同时有函数和方法.方法就是一个包含了接受者(receiver)的函数,re ...

  4. python requests模块session的使用建议及整个会话中的所有cookie的方法

    话不多说,直接上代码 测试代码 服务端 下面是用flask做的一个服务端,用来设置cookie以及打印请求时的请求头 # -*- coding: utf-8 -*- from flask import ...

  5. 第一个SpringBoot测试实例

    1.SpringBoot项目构建:http://start-spring.io   自动化构建SpringBoot项目,保存在本地并解压 2.安装gradle并配置gradle环境 3.配置阿里云ma ...

  6. sql一关联多查询时否定筛选出现的问题的解决

    问题:一方关联多方查询时执行否定筛选,结果包含未通过筛选的项. 我们规定一方为父,多方为子,我们希望子未通过筛选时,结果也不出现对应的父. 查询部门及部门下的所有员工. SELECT * FROM d ...

  7. Metasploit实现木马生成、捆绑、免杀

    原创博客,转载请注出处! 我的公众号,正在建设中,欢迎关注: Meatsploit介绍 2018/01/03 更新 Metasploit是一款优秀的开源(!= 完全免费)渗透测试框架平台,在该平台下可 ...

  8. http-post调用接口简单代码

    一.简单便捷的httpget调用接口,并且返回接口数据1.导入相应的jar包: 2.代码如下: HttpPost post = null; try { HttpClient httpClient = ...

  9. C++标准库(体系结构与内核分析)(侯捷第二讲)

    一.OOP和GP的区别(video7) OOP:面向对象编程(Object-Oriented programming) GP:泛化编程(Generic programming) 对于OOP来说,我们要 ...

  10. css之vw布局

    vw,vh是视口单位,是相对视口单位,与百分百布局不一样的是,百分百是相对于父及元素,而vw布局是相对与窗口. 而rem布局是要与js一起配合 // 以iphone6设计稿 @function px2 ...