为什么80%的码农都做不了架构师?>>>   

zuul核心框架

zuul是可以认为是一种API-Gateway。zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。其原理就是在zuul把Request route到源web-service的时候,处理一些逻辑,比如Authentication,Load Shedding等。 下图是zuul的核心框架。对于框架中的核心类将一一分析。

ZuulFilter

ZuulFilter主要特征如下:

  • Type:定义filter的类别,用字符串代表,有四种标准类别,代表了Request的生命周期。filterType()返回值代表该filter的Type。

    1. PRE: 该类型的filters在Request routing到源web-service之前执行。用来实现Authentication、选择源服务地址等
    2. ROUTING:该类型的filters用于把Request routing到源web-service,源web-service是实现业务逻辑的服务。这里使用HttpClient请求web-service。
    3. POST:该类型的filters在ROUTING返回Response后执行。用来实现对Response结果进行修改,收集统计数据以及把Response传输会客户端。
    4. ERROR:上面三个过程中任何一个出现错误都交由ERROR类型的filters进行处理。
  • Execution Order: 同一个Type的filters组成Pipeline,Execution Order决定他们执行的顺序。filterOrder()返回值是该filter的Execution Order

  • Criteria:定义了filter执行需要满足的条件。对应的方法是shouldFilter()
  • Action: 定了filter处理逻辑。对应的方法是run()

下面是一个 简单的filter示例,该filter用于延迟一个出故障设备的请求。

class DeviceDelayFilter extends ZuulFilter {def static Random rand = new Random()@OverrideString filterType() {return 'pre'}@Overrideint filterOrder() {return 5}@Overrideboolean shouldFilter() {return  RequestContext.getRequest().getParameter("deviceType")?equals("BrokenDevice"):false}@OverrideObject run() {sleep(rand.nextInt(20000)) //Sleep for a random number of seconds//between [0-20]}
}

filter的功能并不具有太多特色,它和Servlet框架的Filter以及AOP功能及角色都很像,应该是zuul的开发者借鉴了这些优秀的设计。 
zuul框架主要的功能就是动态的读取,编译,运行这些filter。filter之间不直接communicate ,他们之间通过RequestContext来共享状态信息,既然filter都是对特定Request的处理,那么RequestContext就是Request的Context,RequestContext用来管理 Request的Context,不受其它Request的影响。 
Filter源码文件放在zuul 服务特定的目录, zuul server会定期扫描目录下的文件的变化。如果有Filter文件更新,源文件会被动态的读取,编译加载进入服务,接下来的Request处理就由这些新加入的filter处理。

Request生命周期

ZuulServlet

Zuul基于Servlet框架,ZuulServlet用于处理所有的Request。其可以认Http Request的入口。 
其类图如下: 

如果对SpringMVC比较熟悉,可以把ZuulServlet类比为DispatcherServlet,所有的Request都要经过ZuulServlet的处理。因此ZuulServlet是zuul框架源码分析的入口点。 
zuul本身不实现Web容器,因此zuul本身其实也就没有太过复杂的线程模型和执行逻辑。不过在此回顾下Servlet框架以及典型Web 容器的线程模型,这也是理解zuul线程模型的关键。如果对SpringMVC比较熟悉的话,那么zuul的在整个web 容器所处的位置基本上和SpringMVC一致。

Servlet的生命周期

Servlet 通过一个定义良好的生命周期来进行管理,该生命周期规定了 Servlet 如何被加载、实例化、初始化、 处理客户端请求,以及何时结束服务。该生命周期可以通过 javax.servlet.Servlet 接口中的 initservice 和 destroy 这些 API 来表示,所有 Servlet 必须直接或间接的实现 GenericServlet 或 HttpServlet 抽象类。 
Servlet的生命周期有四个阶段:加载并实例化初始化请求处理销毁。主要涉及到的方法有initservicedoGetdoPostdestory等。

Web容器线程模型

Servlet只是基于Java技术的web组件,该组件由容器托管,用于生成动态内容。Servlet容器是web Server或application server 的一部分,供基于Request/Response发送模型的网络服务,解码基于MIME的请求,并格式化基于MIME的响应。Servlet容器包含并管理Servlet生命周期。典型的Servlet容器有Tomcat、Jetty。

上图是Tomcat基于NIO的线程模型,其基于典型的Acceptor/Reactor线程模型。

此处不详细分析Tomcat对该线程模型的实现,仅关心与Servlet生命周期相关的部分。在Tomcat的线程模型中,Worker线程用来处理Request。当容器收到一个Request后,调度线程从Worker线程池中选出一个Worker线程,将请求传递给该线程,然后由该线程来执行Servlet的service()方法。且该worker线程只能同时处理一个Request请求,如果过程中发生了阻塞,那么该线程就会被阻塞,而不能去处理其他任务。 
Servlet默认情况下一个单例多线程

回到zuul,由上面的回顾可知,zuul逻辑的入口必定是ZuulServlet.service(ServletRequest servletRequest, ServletResponse servletResponse)。下面看下源代码的简化版 ,只保留逻辑部分,。

    public void service(ServletRequest servletRequest, ServletResponse servletResponse)  {// 用于初始化RequestContextinit((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);/* RequestContext 用于记录Request的context。前面也分析了,由于Servlet是单例多线程的,而Request由唯一 worker线程处理,这里的RequestContext使用`ThreadLocal`实现,其本身简单wrap了`ConcurrentHashMap`,*/RequestContext context = RequestContext.getCurrentContext();// 执行Pre filters逻辑preRoute();// 执行route逻辑route();// 执行postRoute逻辑postRoute();}

RequestContext提供了执行filter Pipeline所需要的Context,因为Servlet是单例多线程,这就要求RequestContext即要线程安全又要Request安全。context使用ThreadLocal保存,这样每个worker线程都有一个与其绑定的RequestContext,因为worker仅能同时处理一个Request,这就保证了Request Context 即是线程安全的由是Request安全的。所谓Request 安全,即该Request的Context不会与其他同时处理Request冲突。 
RequestContext简单wrap 了ConcurrentHashMap吗,没有太过复杂的逻辑,此处不再分析。

三个核心的方法preRoute(),route()postRoute(),zuul对request处理逻辑都在这三个方法里,ZuulServlet交给ZuulRunner去执行。由于ZuulServlet是单例,因此ZuulRunner也仅有一个实例。

ZuulRunner

ZuulRunner直接将执行逻辑交由FilterProcessor处理。 
FilterProcessor也是单例。 

FilterProcessor

其功能就是依据filterType执行filter的处理逻辑,其类图如下。

核心方法runFilters()

    public Object runFilters(String sType) throws Throwable {if (RequestContext.getCurrentContext().debugRouting()) {Debug.addRoutingDebug("Invoking {" + sType + "} type filters");}boolean bResult = false;List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);if (list != null) {for (int i = 0; i < list.size(); i++) {ZuulFilter zuulFilter = list.get(i);Object result = processZuulFilter(zuulFilter);if (result != null && result instanceof Boolean) {bResult |= ((Boolean) result);}}}return bResult;}

每个filter的处理逻辑,仅保留逻辑部分。

    public Object processZuulFilter(ZuulFilter filter) throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();long execTime = 0;String filterName = "";try {long ltime = System.currentTimeMillis();// 运行filter的处理逻辑ZuulFilterResult result = filter.runFilter();ExecutionStatus s = result.getStatus();execTime = System.currentTimeMillis() - ltime;// 记录filter的处理状态,如果filter处理失败,把异常抛出switch (s) {case FAILED:t = result.getException();ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);break;case SUCCESS:o = result.getResult();ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);break;default:break;}if (t != null) throw t;usageNotifier.notify(filter, s);return o;} catch (Throwable e) {usageNotifier.notify(filter, ExecutionStatus.FAILED);}}
  • 上面两段代码是FilterProcessor对filter的处理逻辑。
  • 首先根据Type获取所有输入该Type的filter,List<ZuulFilter> list
  • 遍历该list,执行每个filter的处理逻辑,processZuulFilter(ZuulFilter filter)
  • RequestContext对每个filter的执行状况进行记录,应该留意,此处的执行状态主要包括其执行时间、以及执行成功或者失败,如果执行失败则对异常封装后抛出。 
    到目前为止,zuul框架对每个filter的执行结果都没有太多的处理,它没有把上一filter的执行结果交由下一个将要执行的filter,仅仅是记录执行状态,如果执行失败抛出异常并终止执行。

最后看下方法ZuulFilter.runFilter()

public ZuulFilterResult runFilter() {ZuulFilterResult zr = new ZuulFilterResult();if (!isFilterDisabled()) {if (shouldFilter()) {Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());try {Object res = run();zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);} catch (Throwable e) {t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");zr = new ZuulFilterResult(ExecutionStatus.FAILED);zr.setException(e);} finally {t.stopAndLog();}} else {zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);}}return zr;}

zuul框架对filter的处理到此就结束了。从上面代码可以看出,ZuulFilterResult 记录了该filter的执行状态,run() 中返回的Object其实在zuul框架中没有用到过。

分析到这里,可以看出除去 zuul框架对filter的管理,zuul框架作用有限。而且filter也十分简单,没有Servlet框架中Filter丰富。

总结

  • ZuulServlet是zuul框架的入口,其采用Servlet框架,是单例多线程,可以把它类比为SpringMVC的DispatcherServletZuulServlet定义Request的生命周期内的处理逻辑,每个阶段的具体处理逻辑交由ZuulRunnerFilterProcessor
  • 以上是Request生命周期内的主要逻辑。下面分析Filter生命周期管理。

转载于:https://my.oschina.net/voole/blog/886468

zuul源码分析之Request生命周期管理相关推荐

  1. Spring源码分析——Bean的生命周期

    文章目录 说明 测试代码 说明 本文从源码的角度分析Spring中Bean的加载过程,本文使用的Spring版本为4.3.25.RELEASE 测试代码 测试代码如下,根据这段简单的测试代码,一步步跟 ...

  2. Spring源码分析-深入理解生命周期之BeanFactoryProcessor

    生命周期之BeanFactoryPostProcessor 先来看看bean的生命周期.对于熟悉spring 的朋友来说,bean的生命周期并不陌生.它可以在bean加载,bean初始化的过程中加入我 ...

  3. PHP源码分析-PHP的生命周期

    PHP的最多的两种运行模式是WEB模式.CLI模式. 无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行. 1.当我们在终端敲入php这个命令的时候,它使用的是CLI. 它就像一个web服务 ...

  4. Spring5源码 - 07 Spring Bean 生命周期流程 源码解读02

    文章目录 Pre 通俗流程 finishBeanFactoryInitialization Pre Spring5源码 - 06 Spring Bean 生命周期流程 概述 01 接上文 通俗流程 下 ...

  5. 【vn.py学习笔记(六)】vn.py constant源码阅读、委托生命周期

    [vn.py学习笔记(六)]vn.py constant源码阅读.委托生命周期 写在前面 1 constant 1.1 Direction 1.2 Offset 1.3 Status 1.4 Prod ...

  6. 跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期)

    跟着小马哥学系列之 Spring IoC(源码篇:Bean 生命周期) 简介 Bean 元信息来源 Bean 元信息解析成 BeanDefinition 并注册 BeanDefinition 转变成 ...

  7. javaweb_笔记2(Servlet源码分析;request详解;请求域;转发和重定向;WebServlet注解;jsp基础语法,JavaBean。)

    1.HttpServlet源码分析 HttpServlet类是专门为HTTP协议准备的.比GenericServlet更加适合HTTP协议下的开发. HttpServlet在哪个包下? jakarta ...

  8. Spark源码分析之九:内存管理模型

    Spark是现在很流行的一个基于内存的分布式计算框架,既然是基于内存,那么自然而然的,内存的管理就是Spark存储管理的重中之重了.那么,Spark究竟采用什么样的内存管理模型呢?本文就为大家揭开Sp ...

  9. Spring5源码 - 06 Spring Bean 生命周期流程 概述 01

    文章目录 Bean生命周期概述 Demo finishBeanFactoryInitialization(beanFactory) 核心流程 Bean生命周期概述 说到Spring Bean的生命周期 ...

最新文章

  1. 知识图谱与机器学习如何结合?
  2. postgreSQL怎样创建一个序列号/自动递增的字段
  3. 如何用纯 CSS 创作一个荧光脉冲 loader 特效
  4. 第五节: Quartz.Net五大构件之Trigger的四大触发类
  5. Mysql数据库使用及其问题合集一
  6. 天梯—打印沙漏以及剩余个数(C语言)
  7. MyBatis学习总结[5]-动态 SQL
  8. readelf 和 objdump 例子详解及区别 (ELF文件说明)
  9. 功能测试用例设计方法分享
  10. set_global_opts在PyEcharts中实现全局配置项
  11. 前端要点总结1(2021-12)
  12. js提取字符串中数字的三种方法
  13. 基于centos搭建reviewboard
  14. Nagios和ndo2db系统脚本---for gentoo
  15. 手把手教学 玩转苹果HomeKit
  16. 深度学习入门(三)一种预测阶段使用任意参数的滑动平均的快捷方式
  17. Android蓝牙搜索连接通信
  18. Canvas drawImage在高清屏幕下变模糊,解决方案
  19. 字号、pt、px、inch、cm 之间的关系及对照表
  20. 你有“隐私泄露担忧”吗?适合普通用户的6个方法来了

热门文章

  1. php mian函数,电脑main什么意思
  2. channelinactive触发后不关闭channel_golang chan 最详细原理剖析,全面源码分析!看完不可能不懂的!...
  3. MyBatis映射器出错,已解决(Multiple annotations found at this line:)
  4. 20190822:(leetcode习题)字符串转换整数 (atoi)
  5. python编写代码实现文件的拷贝功能_如何使用Python脚本实现文件拷贝
  6. android面试 源码,Android面试题-onCreate源码都没看过,怎好意思说自己做android-Go语言中文社区...
  7. java读取合并单元格_Java POI常用方法,读取单元格的值,设置单元格格式,合并单元格,获取已合并的单元格,导出至本地等...
  8. ajax同步、异步区别及应用
  9. 嵌入式设备中支持国密算法的方法
  10. LINUX PATH环境变量