zuul 1.x 源码阅读之 ZuulServlet 及 Filter 加载

本文详细介绍了Zuul作为API Gateway的角色及其工作原理。深入源码,分析了ZuulServlet如何处理HTTP请求,ZuulRunner如何串联过滤器,以及FilterLoader如何动态加载和管理过滤器。通过ZuulServlet的service方法,理解了request和response在filter之间的传递方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原创: http://www.imet.me/post/2018/07-03_zuul_1.x_source/

zuul 是什么,为什么需要 zuul,zuul 实现原理?

一、zuul overview

1.1 什么是 Zuul?

zuul 作为云服务的边界服务(Edge Service),可以用来做统一授权、压力测试、金丝雀测试(Canary Testing)、动态路由、减负载以及和 netflix 其他套件一起协作

1.2 zuul as edge service

直接用官方用图:

Zuul in Netflix’s Cloud Architecture

如上图,zuul 处于内部服务的入口,起到边界服务(Edge Service)的作用

1.3 zuul 的工作原理

先不解释看官方图:

Zuul Core Architecture

分析一下 zuul 的思路:

  1. ZuulServlet: zuul 的核心其实就是 ZuulServlet, 在 NIO 之前, java web 服务都是以 Servlet 作为入口提供服务的。
  2. ZuulFilter Runner: 通过 Runner 来串起来不同类型的 filter, 一个 http request 经过 “pre”, “route”, “post” 这三种类型的 filter。有点 servlet filter 的味道
  3. Filter: 可以在 Filter 里执行业务需要的逻辑。Filter 支持 java 类型的 filter 和 groovy 类型的 filter
  4. Filter Loader: 如果要做到动态添加或者移除 filter 的话,groovy 脚本动态编译是个不错的选择。通过监听指定目录,然后生成 filter 类,装载到 zuul runner
  5. Request Context: filter 如何获取 http request/response 呢。猜对了,ThreadLocal, 通过把 HttpRequest 和 HttpResposne Wrapper 起来,放到 threadlocal 变量里。

二、zuul-core + zuul-simple-webapp 源码解读之 ZuulServlet

先跑起工程再说,跑起 zuul-simple-webapp 工程,参考官方wiki

2.1 web.xml 声明

web.xml 声明完成了以下几件事情:

  • StartServer(ServletContextListener): 是 web 容器的监听器,在容器 context 初始化时做了以下几件事情:

    1. mock monitor
    2. 初始化 FilterFileManager: 去系统变量 zuul.filter.root 目录下定时检查是否有新的脚本
    3. 初始化 JavaFilter
  • ZuulServlet: 是 Servlet 时代处理 http 请求的主要主体。我们源码会分析

  • ContextLifecycleFilter: 主要是及时清理 threadLocal 相关的变量: ZuulServlet 为了使 ZuulFilter 能获取Request相关的 Context, 把 requestContext 放到 threadlocal 中。

<listener>
    <listener-class>com.netflix.zuul.StartServer</listener-class>
</listener>

<servlet>
    <servlet-name>Zuul</servlet-name>
    <servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>Zuul</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>ContextLifecycleFilter</filter-name>
    <filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>ContextLifecycleFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.2 ZuulServlet

a.) ZuulServlet 是继承 HttpServlet

通过实现 Servlet 的 initservic 来分别完成初始化和处理 http 逻辑。

b.) init 方法中创建 ZuulRunner 对象。
  1. 查看 Config param 里是否包含 buffer-requests
  2. ZuulServlet 的实际执行都是在 ZuulRunner 去处理的
c.) service 方法干了以下几件事情
  • 通过 ZuulRunner 初始化 request

.ZuulRunner.java

public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
    
    
  // 主要就是把 request 和 resposne 放到 requestContext 的 ThreadLocal 变量里
  RequestContext ctx = RequestContext.getCurrentContext();
  if (bufferRequests) {
    
    
      ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
  } else {
    
    
      ctx.setRequest(servletRequest);
  }

  ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
}
  • RequestContext 是以 ConcurrentHashMap 来存储 http 请求中 header,body 等各种参数的
  • 同时通过 RequestContext.getCurrentContext() 来获取 threadlocal 中的 context

.RequestContext.java

public class RequestContext extends ConcurrentHashMap<String, Object> {
    
    

    protected static Class<? extends RequestContext> contextClass = RequestContext.class;

    private static RequestContext testContext = null;

    protected static
<think>嗯,用户现在想了解Zuul 1.x版本中线程池隔离的实现方式、配置方法以及原理。首先,我需要回忆一下Zuul 1.x的相关知识。记得Zuul是Netflix的网关服务,负责路由、监控、安全等。在微服务架构中,它作为前置网关处理所有请求。 用户提到的线程池隔离,可能和Hystrix有关,因为Zuul默认集成了Hystrix来实现容错和隔离。Hystrix有两种隔离策略:线程池和信号量。默认情况下,Zuul使用的是信号量隔离,但用户可能想了解如何配置成线程池隔离。 根据引用[2],Zuul默认使用信号量隔离,而Hystrix默认是线程池。所以用户的问题可能涉及到如何将Zuul的隔离策略从信号量改为线程池。需要查看配置属性,比如zuul.ribbon-isolation.strategy,默认是semaphore,改为thread的话应该就能启用线程池隔离。 另外,线程池隔离的原理是通过为每个依赖服务分配独立的线程池,避免一个服务的延迟影响到其他服务。Zuul在处理路由转发时,会使用Hystrix的Command模式,将请求封装成HystrixCommand,这样就能利用线程池来隔离不同的请求。 配置方面,需要设置zuul.ribbon-isolation.strategy=thread,同时调整线程池的大小,比如hystrix.threadpool.default.coreSize和maxQueueSize。这些参数控制线程池的核心线程数和队列容量,防止资源耗尽。 还要注意,线程池隔离会增加上下文切换的开销,可能影响性能,所以需要根据实际场景权衡。信号量隔离更适合内部调用,而线程池适合外部依赖,防止雪崩效应。 用户可能还关心如何验证配置是否生效,或者如何监控线程池的状态。这部分可能需要结合Hystrix Dashboard或Turbine进行监控,但用户的问题主要集中在配置和原理,所以可能需要简要提及。 总结一下,回答应该包括线程池隔离的原理、配置方法(属性设置)、与信号量隔离的区别,以及注意事项。同时,需要引用相关的引用内容,比如引用[2]中提到Zuul默认使用信号量,而Hystrix默认是线程池,说明需要显式配置才能切换。</think>### Zuul 1.x 线程池隔离的实现与配置 #### 一、线程池隔离的实现原理 Zuul 1.x 默认集成 Hystrix 实现服务容错和隔离,其隔离策略支持两种模式:**信号量(Semaphore)**和**线程池(Thread Pool)**。 - **线程池隔离**通过为每个服务依赖分配独立的线程池,避免因某个服务延迟导致线程资源耗尽,从而保障系统稳定性[^2]。 - **Hystrix Command**:Zuul 的路由转发逻辑会被 Hystrix 封装为 `HystrixCommand`,通过线程池执行请求,实现资源隔离[^3]。 #### 二、配置线程池隔离的步骤 1. **启用线程池隔离策略** 在配置文件中设置隔离策略为 `thread`: ```properties zuul.ribbon-isolation-strategy=thread ``` 2. **配置线程池参数** 调整 Hystrix 线程池的核心参数: ```properties # 线程池核心线程数 hystrix.threadpool.default.coreSize=20 # 最大队列容量(默认-1使用SynchronousQueue,建议设为有限值) hystrix.threadpool.default.maxQueueSize=100 # 队列拒绝阈值(当maxQueueSize > 0时生效) hystrix.threadpool.default.queueSizeRejectionThreshold=50 ``` 3. **超时时间配置** 设置 Hystrix 超时时间(需大于Ribbon超时): ```properties hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 ``` #### 三、线程池隔离 vs 信号量隔离 | **特性** | **线程池隔离** | **信号量隔离** | |----------------|-------------------------------|-------------------------| | 资源消耗 | 高(需维护线程池) | 低(基于计数器) | | 适用场景 | 外部服务调用(如HTTP请求) | 内部高速调用(如内存操作) | | 阻塞影响 | 线程池满则拒绝请求 | 计数器满则直接拒绝 | | 配置复杂度 | 需调整线程池参数 | 仅需设置信号量大小 | #### 四、注意事项 1. **性能权衡**:线程池隔离会增加上下文切换开销,需根据业务负载调整核心线程数[^2]。 2. **队列管理**:避免无界队列导致内存溢出,建议设置 `maxQueueSize` 并监控队列堆积。 3. **与信号量对比**:若网关主要处理轻量级转发,信号量隔离(默认)可能更高效[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值