Springcloud的版本是Greenwich.SR2,Springboot版本是2.1.6.release.

最近使用到Springcloud的zuul,分析了下源码,记录下。

如下List-1,我们自己定义的ZuulFilter继承zuul的zuulFilter,之后定义为Bean,交给Spring容器:

List-1

//将过滤器交给Spring管理
@Bean
public AuthFilter authFilter(){return new AuthFilter();
}//xss过滤
@Bean
public XssFilter xssFilter(){return new XssFilter();
}@Bean
public HelloZuulFilter firewallFilter(){return new HelloZuulFilter();
}@Bean
public HelloInfoFilter helloInfoFilter(){return new HelloInfoFilter();
}

之后看下ZuulServerAutoConfiguration,如下List-2,@Autowired private Map<String, ZuulFilter> filters会从Spring容器中获取所有的ZuulFilter,之后实例化ZuulFilterInitializer时,将这个filters传入。

List-2

...
@Configuration
protected static class ZuulFilterConfiguration {@Autowiredprivate Map<String, ZuulFilter> filters;@Beanpublic ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,TracerFactory tracerFactory) {FilterLoader filterLoader = FilterLoader.getInstance();FilterRegistry filterRegistry = FilterRegistry.instance();return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,filterLoader, filterRegistry);}
}
...

如下List-3,

  1. FilterRegistry是属性类型,contextInitialized方法上加了@PostConstruct,所以Spring创建这个Bean之后就会调用这个方法,遍历filters之后,放入filterRegistry中,filterRegistry中有个ConcurrentHashMap类型的属性,这些filter就是放入这个ConcurrentHashMap中。
  2. 方法contextDestroyed上加了@PreDestroy注解,之后遍历filters,将其从filterRegistry中移除。

List-3

public class ZuulFilterInitializer {private static final Log log = LogFactory.getLog(ZuulFilterInitializer.class);private final Map<String, ZuulFilter> filters;private final CounterFactory counterFactory;private final TracerFactory tracerFactory;private final FilterLoader filterLoader;private final FilterRegistry filterRegistry;public ZuulFilterInitializer(Map<String, ZuulFilter> filters,CounterFactory counterFactory, TracerFactory tracerFactory,FilterLoader filterLoader, FilterRegistry filterRegistry) {this.filters = filters;this.counterFactory = counterFactory;this.tracerFactory = tracerFactory;this.filterLoader = filterLoader;this.filterRegistry = filterRegistry;}@PostConstructpublic void contextInitialized() {log.info("Starting filter initializer");TracerFactory.initialize(tracerFactory);CounterFactory.initialize(counterFactory);for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {filterRegistry.put(entry.getKey(), entry.getValue());}}@PreDestroypublic void contextDestroyed() {log.info("Stopping filter initializer");for (Map.Entry<String, ZuulFilter> entry : this.filters.entrySet()) {filterRegistry.remove(entry.getKey());}clearLoaderCache();TracerFactory.initialize(null);CounterFactory.initialize(null);}
...

默认会将ZuulServlet或者ZuulServletFilter注入到Spring容器中,如下如果设置zuul.use-filter为true,那么使用的是ZuulServletFilter,默认没有设置zuul.use-filter,所以使用的是ZuulServlet,如下List-4,ZuulServlet继承了HttpServlet,是个Servlet,之后交给ServletRegistrationBean,将这个ZuulServlet放入到web容器中。

List-4

...
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
public ServletRegistrationBean zuulServlet() {ServletRegistrationBean<ZuulServlet> servlet = new ServletRegistrationBean<>(new ZuulServlet(), this.zuulProperties.getServletPattern());// The whole point of exposing this servlet is to provide a route that doesn't// buffer requests.servlet.addInitParameter("buffer-requests", "false");return servlet;
}@Bean
@ConditionalOnMissingBean(name = "zuulServletFilter")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true", matchIfMissing = false)
public FilterRegistrationBean zuulServletFilter() {final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>();filterRegistration.setUrlPatterns(Collections.singleton(this.zuulProperties.getServletPattern()));filterRegistration.setFilter(new ZuulServletFilter());filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE);// The whole point of exposing this servlet is to provide a route that doesn't// buffer requests.filterRegistration.addInitParameter("buffer-requests", "false");return filterRegistration;
}
...

ZuulServlet的service方法如下,首先会调用preRoute()方法,之后调用route(),最后是postRoute(),preRoute方法调用了zuulRunner.preRoute(),ZuulRunner的preRoute()方法里面调用了FilterProcessor.getInstance().preRoute(),再深入FilterProcessor的preRoute方法,

List-5

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {try {this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);RequestContext context = RequestContext.getCurrentContext();context.setZuulEngineRan();try {this.preRoute();} catch (ZuulException var13) {this.error(var13);this.postRoute();return;}try {this.route();} catch (ZuulException var12) {this.error(var12);this.postRoute();return;}try {this.postRoute();} catch (ZuulException var11) {this.error(var11);}} catch (Throwable var14) {this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));} finally {RequestContext.getCurrentContext().unset();}
}void postRoute() throws ZuulException {this.zuulRunner.postRoute();
}void route() throws ZuulException {this.zuulRunner.route();
}void preRoute() throws ZuulException {this.zuulRunner.preRoute();
}

FilterProcessor的preRoute()里面的代码如下List-6,调用runFilters方法,从FilterRegistry取出所有filterType是pre的所有ZuulFilter,之后进行排序,之后逐个调用ZuulFilter的runFilter方法——在方法processZuulFilter里面。ZuulFilter是个抽象类,runFilter方法里面调用了run方法,run方法是抽象方法,由我们自定义实现,如下List-7所示,

List-6

...
public void preRoute() throws ZuulException {try {this.runFilters("pre");} catch (ZuulException var2) {throw var2;} catch (Throwable var3) {throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());}
}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 = (ZuulFilter)list.get(i);Object result = this.processZuulFilter(zuulFilter);if (result != null && result instanceof Boolean) {bResult |= (Boolean)result;}}}return bResult;
}public Object processZuulFilter(ZuulFilter filter) throws ZuulException {RequestContext ctx = RequestContext.getCurrentContext();boolean bDebug = ctx.debugRouting();String metricPrefix = "zuul.filter-";long execTime = 0L;String filterName = "";try {long ltime = System.currentTimeMillis();filterName = filter.getClass().getSimpleName();RequestContext copy = null;Object o = null;Throwable t = null;if (bDebug) {Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);copy = ctx.copy();}ZuulFilterResult result = filter.runFilter();ExecutionStatus s = result.getStatus();execTime = System.currentTimeMillis() - ltime;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);if (bDebug) {Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");Debug.compareContextState(filterName, copy);}}if (t != null) {throw t;} else {this.usageNotifier.notify(filter, s);return o;}} catch (Throwable var15) {if (bDebug) {Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + var15.getMessage());}this.usageNotifier.notify(filter, ExecutionStatus.FAILED);if (var15 instanceof ZuulException) {throw (ZuulException)var15;} else {ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);throw ex;}}
}
...

如下List-7所示,用try catch方式来调用run方法,如果run方法抛出异常,则视为失败,将ZuulFilterResult的ExecutionStatus设置为FAILED,所以我们实现的run方法返回什么值并不重要,重要的是不抛出异常,如果抛出异常则视为处理失败。

List-7

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

要注意的是,上面中涉及到的FilterRegistry引用的都是同一个静态变量,所以各个调用关系见不显示的传递,依然能确保线程安全。

private static final FilterRegistry INSTANCE = new FilterRegistry();

需要注意的是,ZuulServlet和ZuulServletFilter处理的是url为/zuul/*的请求,可以看List-4的this.zuulProperties.getServletPattern(),它的值就是/zuul,之后用ServletRegistration.Dynamic的addMapping方法加上处理的url为/zuul/*的。

Reference

  1. 源码

转载于:https://my.oschina.net/u/2518341/blog/3068771

Springcloud之zuul的ZuulFilter相关推荐

  1. SpringCloud→SpringCloud搭建Zuul动态服务网关(五)

    文章目录 SpringCloud搭建Zuul动态服务网关 网关分类 开放Api 微服务网关 API服务管理平台 网关设计 开放API接口 内网API接口 网关框架 使用Zuul实现反向代理 使用Zuu ...

  2. 10.Springcloud的Zuul

    示例代码: github地址 Springcloud的Zuul概述 Zuul包含了对请求的路由和过滤两个最主要的功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外部访问统一入口的基础 ...

  3. Spring-Cloud 微服务网关Zuul、ZuulFilter过滤器和限流

    微服务网关 一. Zuul网关 1. 创建工程 并导入依赖 2. application.yml 配置文件 3. 启动类添加注解 4. 依次启动服务 5. 进入浏览器访问测试 二. ZuulFilte ...

  4. properties 配置回车_非常全面的讲解SpringCloud中Zuul网关原理及其配置,看它就够了!...

    本文同步Java知音社区,专注于Java 作者:kosamino http://www.cnblogs.com/jing99/p/11696192.html Zuul是spring cloud中的微服 ...

  5. SpringCloud系列——Zuul 动态路由

    前言 Zuul 是在Spring Cloud Netflix平台上提供动态路由,监控,弹性,安全等边缘服务的框架,是Netflix基于jvm的路由器和服务器端负载均衡器,相当于是设备和 Netflix ...

  6. 从零开始搭建spring-cloud(5) ----zuul

    撸了今年阿里.头条和美团的面试,我有一个重要发现.......>>> zuul是什么 zuul 是netflix开源的一个API Gateway 服务器, 本质上是一个web ser ...

  7. 非常全面的讲解SpringCloud中Zuul网关原理及其配置,看它就够了!

    作者:kosamino www.cnblogs.com/jing99/p/11696192.html Zuul是spring cloud中的微服务网关.网关:是一个网络整体系统中的前置门户入口.请求首 ...

  8. properties 配置回车_非常全面的讲解SpringCloud中Zuul网关原理及其配置,看它就够了! - 风平浪静如码

    Zuul是spring cloud中的微服务网关.网关:是一个网络整体系统中的前置门户入口.请求首先通过网关,进行路径的路由,定位到具体的服务节点上. Zuul是一个微服务网关,首先是一个微服务.也是 ...

  9. 全面的讲解SpringCloud中Zuul网关原理及其配置,看它就够了

    Zuul是spring cloud中的微服务网关.网关:是一个网络整体系统中的前置门户入口.请求首先通过网关,进行路径的路由,定位到具体的服务节点上. Zuul是一个微服务网关,首先是一个微服务.也是 ...

最新文章

  1. list.add时报错:Exception in thread “main“ java.lang.UnsupportedOperationException
  2. 3行代码实现从excel中读取出某列元素为所想要的元素集合中的所有行
  3. Java常见内存溢出异常分析
  4. java开发环境及数据类型实验_实验项目1 Java开发环境与语言基础
  5. [object detection] TypeError: can't pickle dict_values objects
  6. php返回支付状态,magento paypal返回支付状态
  7. 用Java语言编写的特殊算法
  8. 容器编排技术 -- Kubernetes Namespaces
  9. Taurus入门,这么神奇的性能测试工具不能不知道
  10. 决策单调性Ⅱ:斜率优化(1597: [Usaco2008 Mar]土地购买)
  11. oracle 错误代码问题处理经验汇总
  12. java 枚举嵌套枚举_java – 如何使用枚举与分组和分组层次/嵌套
  13. eclipse更换jdk版本
  14. Ubuntu下安装小企鹅fcitx输入法
  15. Yii2中如何使用CodeCeption
  16. COMSOL弱形式解微分方程
  17. Vmware虚拟机Linux配置固定IP地址(详细版)
  18. 【原理】Basic Integer Overflows
  19. 远程登录Linux服务器
  20. 李小龙:我不怕练一万招的人,只怕把一招练一万遍的人

热门文章

  1. flask 项目启动报错:OSError OSError: [Errno 22] Invalid argument
  2. linux 6.5 xen,centos7或者centos6.5安装xen教程(折腾了几天的心血)
  3. 【datart】图表插件开发大赛
  4. 51单片机红外收发器的编码与解码
  5. Proteus 8微机接口仿真模拟实验,8086,8255A,DAC0832输出四种波形
  6. String的equals方法原理
  7. 前端常见的性能优化总结
  8. photoshop(抠图)常用快捷键
  9. 二分查找算法介绍及实现
  10. 深入浅出Spring Cloud Netflix - Ribbon