spring-cloud-netflix-zuul源码解析

基于spring-cloud-netflix-zuul-2.2.5.RELEASE.jar,zuul版本1.3.1。

1. 前言

本文注定发表即过时,毕竟对于极致追求性能的网关来说,外有 spring-cloud-starter-gateway,内有新版本的zuul 2.x,所以本文主要作为过往相关研究的阶段性总结,直接一勺烩,不再做逐一概念解读,骗篇幅了。

2. SpringBoot之AutoConfig

既然是基于 spring-cloud-netflix-zuul 进行解读,那 SpringBoot源码解析之AutoConfiguration 就是绕不过去的了。

spring-cloud-netflix-zuul 下的 META-INF/spring.factories 文件中引入了ZuulServerAutoConfigurationZuulProxyAutoConfiguration 两个自动配置类。

  1. ZuulProxyAutoConfigurationZuulServerAutoConfiguration的子类。可以理解为ZuulProxyAutoConfigurationZuulServerAutoConfiguration的升级版。
  2. ZuulProxyAutoConfiguration是否生效取决于 ZuulProxyMarkerConfiguration.Marker 实例存在于容器中。而向容器中注入Marker实例的操作位于 ZuulProxyMarkerConfiguration 这个配置类中。这个配置类被容器扫描到的操作则是在注解@EnableZuulProxy中完成的。这个落地同样适用于ZuulServerAutoConfiguration的启用。最终形成的对应关系:
    a. ZuulProxyAutoConfiguration + @EnableZuulProxy + ZuulProxyMarkerConfiguration
    b. ZuulServerAutoConfiguration + @EnableZuulServer + ZuulServerMarkerConfiguration
  3. 所以,如果你想要启用高配版ZuulProxyAutoConfiguration,请在项目中使用注解@EnableZuulProxy,否则使用@EnableZuulServer

作为子类,ZuulProxyAutoConfiguration天然继承了父类ZuulServerAutoConfiguration中所定义的Bean,所以接下来我们就以ZuulServerAutoConfiguration为例,进行相关讲解。

2.1 ZuulProxyAutoConfiguration引入的典型Bean

Bean 源自 备注
HasFeatures ZuulProxyAutoConfiguration 。覆盖自ZuulServerAutoConfiguration SpringBoot提供的 actuator特性,对应于 spring-cloud-commons 中的 FeaturesEndpoint。
DiscoveryClientRouteLocator ZuulProxyAutoConfiguration 基于服务发现实现的RouteLocator实现类
ServiceRouteMapper ZuulProxyAutoConfiguration 1. 为 service Id 和 route 之间建立一个转换。默认实现为两者相同的SimpleServiceRouteMapper
2. 应用位置DiscoveryClientRouteLocator.locateRoutes()
ProxyRequestHelper ZuulProxyAutoConfiguration 辅助类,提供各类工具方法。存在子类TraceProxyRequestHelper
PreDecorationFilter / RibbonRoutingFilter / SimpleHostRoutingFilter ZuulProxyAutoConfiguration route相关的ZuulFilter,之后进行专门的讲解。
ZuulController + ZuulHandlerMapping ZuulServerAutoConfiguration 1. 基于SpringMVC的HandlerMapping扩展,将Zuul处理流程集成到SpringMVC主体处理流程中。
2. 正是这两的配合,将 用户请求url 与 自己配置的处理filter 实际联系上的。
ZuulRefreshListener ZuulServerAutoConfiguration 响应事件,触发ZuulHandlerMapping的 handler重新注册逻辑
ZuulServlet/ ZuulServletFilter ZuulServerAutoConfiguration 1. 根据配置zuul.use-filter=true/false二选一,默认为ZuulServlet。
2. ZuulServlet默认响应路径为http://ip:port/zuul/{zuul service name}/yyy/zzz (等同于访问http://ip:port/{zuul service name}/yyy/zzz路径,请求逻辑也会进行zuul处理流程)
3. ZuulServlet默认将和DispatcherServlet平级。
ServletDetectionFilter/ FormBodyWrapperFilter/ DebugFilter / Servlet30WrapperFilter / SendResponseFilter / SendErrorFilter / SendForwardFilter ZuulServerAutoConfiguration 默认注册的基础ZuulFilter实例,之后进行专门讲解
ZuulRouteApplicationContextInitializer ZuulServerAutoConfiguration 启动时就初始化 ribbon 相关的子容器。子容器名为所有注册的服务serviceId 。
ZuulFilterInitializer ZuulServerAutoConfiguration 收集容器中的ZuulFilter实现类,注册到 FilterRegistry中。

2.2 其它注意事项

  1. 启用ZuulProxyAutoConfiguration 时候,顺带就启用了Ribbon。
  2. @EnableZuulProxy默认引入的注解 @EnableCircuitBreaker。可以使用配置spring.cloud.circuit.breaker.enabled进行禁用。而在启用情况下,基于Hystrix,对应使用的是HystrixCircuitBreakerConfiguration(参见spring-cloud-netflix-hystrix-2.2.5.RELEASE.jar中的META-INFO/spring.factories 文件)。

3. Zuul 执行时

基于 ZuulHandlerMapping + ZuulController 将Zuul处理流程植入到SpringMVC主体流程,之后的用户请求,将被SpringMVC调度给ZuulServlet,最终进入Zuul处理流程。

一图胜千言,以下为Zuul 执行时序图
在这里插入图片描述

3.1 关于ZuulServlet

  1. 其直接实现了javax.servlet.http.HttpServlet。所以在层级上它和DispatcherServlet是平齐了。
  2. ZuulServlet实现的service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse)方法中捕获异常类型为Throwable,所以基本可以认定进入zuul处理流程的请求,不会有异常向上抛出给SpringMVC层
  3. 它的这个异常处理思路可以借鉴下: 自己内部产生的异常统一封装为ZuulException方便进行个性化处理,其它异常则向上抛出作统一处理。

4. Zuul Filter

上述流程图中,处于突出主体考虑,我们没有详细绘制出Zuul Filters。但作为灵魂所在,Zuul 对于 Filters的设计思路值得单独作为一个小节。

  1. Zuul Filter的执行被拆分为作为主体的pre, route, post阶段,以及发生异常时候执行的error阶段。
  2. 用户通过实现ZuulFilter抽象类来参与到Zuul的处理流程中,其中必须实现的四个抽象方法:
    a. shouldFilter() 。决定在本次请求处理中,当前Filter是否需要执行。
    b. run()。本Filter的主体执行逻辑,当shouldFilter() 返回true时执行。
    c. filterType()。本Filter的执行阶段。可选:pre, route, post, error。(范例参见:FilterConstants.XXX_TYPE
    d. filterOrder()。在每个执行阶段,本Filter的执行优先级。
  3. zuul filters执行抛出异常时候:
    a. pre, route filters执行抛出异常时,先执行 error filters,再执行 post filters。
    b. post filters执行抛出异常时,直接执行 error filters。

4.1 内置ZuulFilter

针对常见的需求,spring-cloud-netflix-zuul提供了一系列开箱即用的filter。

Filter名称 类别 优先级 说明
ServletDetectionFilter pre -3 检测当前请求是通过DispatcherServlet,还是ZuulServlet运行处理的。
Servlet30WrapperFilter pre -2 对原始的HttpServletRequest进行包装。
FormBodyWrapperFilter pre -1 将Content-Type为application/x-www-form-urlencoded或multipart/form-data的请求包装成FormBodyRequestWrapper对象。
DebugFilter pre 1 1. 根据zuul.debug.request的配置,或当前请求参数中包含"debug=true/false"来决定是否打印debug日志。
2. 在自定义pre Filter时候,注意优先级的设置,确保在DebugFilter之后执行。
PreDecorationFilter pre 5 对当前请求进行预处理以便执行后续操作。最主要的是针对当前请求url,向执行上下文RequestContext中塞入一个名为FilterConstants.SERVICE_ID_KEY的键值对,供后续使用(典型的如:RibbonRoutingFilter)。
RibbonRoutingFilter route 10 通过Ribbon和Hystrix来向服务实例发起请求,并将请求结果进行返回。
SimpleHostRoutingFilter route 100 只对请求上下文中有routeHost参数的进行处理,直接使用HttpClient向routeHost对应的物理地址进行转发。
SendForwardFilter route 500 只对请求上下文中有forward.to参数的进行处理,进行本地跳转。
LocationRewriteFilter post 900 当返回响应状态码3xx时,重写response header中的 Location 值。
SendResponseFilter post 1000 利用请求上下文的响应信息来组织请求成功的响应内容。
SendErrorFilter error 0 当其他过滤器内部发生异常时的会由它来进行处理,产生错误响应。

4.2 Zuul Filter处理流程

直接上图:Zuul - ZuulFilter处理流程
Zuul - ZuulFilter处理流程

5. 关于actuate

spring-cloud-netflix-zuul 提供了对于 actuate特性的支持。

URL短路径 说明 备注 对应Endpoint
/routes 生效的映射 GET请求 RoutesEndpoint
/routes/details 更为细致的Route信息说明 GET请求 RoutesEndpoint
/routes/reset 重置(慎用 1. POST请求调用。
2. 外界通过响应 RoutesRefreshedEvent 进行自定义逻辑处理。
RoutesEndpoint
/filters 生效的filter GET请求 FiltersEndpoint

6. 关于groovy(动态Filter)

Zuul提供了一个能够对过滤器进行动态的加载、编译、运行的框架。这些过滤器是由Groovy写成,被放在Zuul Server上的特定目录下面。Zuul会周期性轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中。这样如果要对过滤器有改动,就不用进行网关的重新发布了,只需要把过滤器上传到指定目录即可。

6.1 使用

  1. 引入 groovy-all 依赖。
	<dependency>
		<groupId>org.codehaus.groovy</groupId>
		<artifactId>groovy-all</artifactId>
		<version>2.5.0-beta-2</version>
	</dependency>
  1. 初始化zuul groovy相关组件。(将以下方法设置为应用启动时候调用即可)
	// initializes groovy filesystem poller (Groovy文件夹 )
	private void initGroovyFilterManager() {
    
    
		FilterLoader.getInstance().setCompiler(new GroovyCompiler());

		//读取配置,获取脚本根目录
		String scriptRoot = System.getProperty("zuul.filter.root", "groovy/filters");		
		if (scriptRoot.length() > 0) {
    
    
			scriptRoot = scriptRoot + File.separator;
		}
		final List<String> filterFolderPaths = CollectionUtil.newArrayList(scriptRoot + "pre", scriptRoot + "route",
				scriptRoot + "post");
		filterFolderPaths.forEach(FileUtil::mkdir);
		
		//获取刷新间隔
        String refreshInterval = System.getProperty("zuul.filter.refreshInterval", "5");
        
		try {
    
    
			FilterFileManager.setFilenameFilter(new GroovyFileFilter());
			FilterFileManager.init(Integer.parseInt(refreshInterval), filterFolderPaths.toArray(new String[] {
    
    }));
		} catch (Exception e) {
    
    
			throw new RuntimeException(e);
		}
	}

6.2 注意

  1. 只支持新增和修改,不支持删除。
  2. 关键类型:
    a. FilterFileManager。起专门的,名为GroovyFilterFileManagerPoller的后台线程,周期性轮询指定Groovy文件夹,动态加载Groovy编写的ZuulFilter。
    b. FilterLoaderFilterFileManager周期性更新的正是这个实例。而 负责进行ZuulFilter调度执行的FilterProcessor 也正是从该实例中获取处理当前请求的ZuulFilter集合,闭环。
    c. GroovyCompiler。负责将groovy脚本文件编译为Class。(在 FilterLoader 中使用)
    d. DefaultFilterFactory 。负责将 Class 实例化为 ZuulFilter实例。(在 FilterLoader 中使用)

7. 常见配置项

zuul:
  decodeUrl: true     # 默认为true, 生效位置: ProxyRequestHelper.java
  forceOriginalQueryStringEncoding: false  # 默认为false, 生效位置: SimpleHostRoutingFilter
  ignoredPatterns:
    - xx
    - yy
  prefix:   # 生效位置: SimpleRouteLocator
  retryable:    # 
  # 将zuul执行路由的调试性信息包含在response的 header中, key为FilterConstants.X_ZUUL_DEBUG_HEADER
  includeDebugHeader: true
  # DebugFilter中生效
  debug:
    request: true
  # ZuulRouteApplicationContextInitializer
  ribbon:
    eager-load:
      enabled: false
######################################################################### Hystrix
  # Isolation strategy to use when executing a {@link HystrixCommand}. 
  # 调用 HystrixCommand 时候的隔离策略 
  # HystrixCommand典型实现类: OkHttpRibbonCommand
  # ===== 默认配置为: SEMAPHORE
  ribbon-isolation-strategy: THREAD
  # 信号量
  semaphore:
    maxSemaphores: 100
  # 线程池, 只在 ribbon-isolation-strategy 设置为 THREAD 时候起效
  #		在 AbstractRibbonCommand 类中生效, 赋值给 hystrix
  threadPool:
    useSeparateThreadPools: false
    threadPoolKeyPrefix: ''  
######################################################################### zuul的超时时间配置 (好像只在 SimpleHostRoutingFilter 中生效)
  # (好像只在 SimpleHostRoutingFilter 中生效)
  host:
    # 最大连接数。默认值是200,我们项目配置的是2000。
    maxTotalConnections: 200
    # 每个路由的最大连接数。默认20,我们项目配置的是500。
    maxPerRouteConnections: 20
    # 
    socket-timeout-millis: 10000
    connect-timeout-millis: 2000
    connectionRequestTimeoutMillis: -1

8. 参考

  1. Github - Zuul1.x
  2. spring-cloud-netflix官网 - multi__router_and_filter_zuul
  3. spring cloud zuul 原理简介及使用
  4. zuul实现所有接口对于带指定前缀和不带前缀的url均能兼容访问
  5. 如何评价 spring cloud gateway? 对比 zuul2.0 主要的优势是什么?

猜你喜欢

转载自blog.csdn.net/lqzkcx3/article/details/124488894