目录
1. 什么是Zuul?
Zuul是从设备、网站到应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul旨在实现动态路由,监控,弹性和安全性。
Zuul使用了各种不同类型的过滤器,这使我们能够快速灵活地将功能应用于边缘服务。这些过滤器帮助我们执行以下功能:
- 安全认证-识别每个资源的身份验证要求,并拒绝不满足要求的请求。
- 日志监控-跟踪某些数据和统计信息,以便为我们提供准确的生产视图。
- 路由转发-根据需要将请求动态路由到不同的后端群集。
- 压力测试-逐渐增加到群集的流量以评估性能。
- 限流-为每种类型的请求分配容量,并丢弃超出限制的请求。
- 熔断-直接在边缘构建一些响应,而不是将其转发到内部集群
Zuul 在 Netflix 的应用场景:
2. Zuul深入剖析
Zuul 是基于 Servlet 开发的,通过 Zuul 的核心构架图可以看出 Zuul 是由很多的过滤器组成的。
2.1 过滤器
Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。
Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期。
- PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
- ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
- POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
- ERROR:在其他阶段发生错误时执行该过滤器。
除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
2.2 源码理解
- Zuul 是如何将各个 Filter 的处理结果连接起来的?
答:通过 RequestContext 对象,使用 ThreadLocal。
关键源码:
public class RequestContext extends ConcurrentHashMap<String, Object> {
protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
@Override
protected RequestContext initialValue() {
try {
return contextClass.newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
};
public static RequestContext getCurrentContext() {
if (testContext != null) return testContext;
RequestContext context = threadLocal.get();
return context;
}
public StringBuilder getFilterExecutionSummary() {
if (get("executedFilters") == null) {
putIfAbsent("executedFilters", new StringBuilder());
}
return (StringBuilder) get("executedFilters");
}
public void unset() {
threadLocal.remove();
}
}
3. Zuul应用场景
红绿部署、开发者测试分支、埋点测试、压力测试、调试路由、金丝雀测试、粘性金丝雀、失败注入测试、降级测试、跨区域高可用、防爬防攻击、健康检查和屏蔽坏节点。
以下是我在学习过程中的几个思考。
3.1 Zuul如何实现路由转发?
通过实现 Filter 来实现:
过滤器样例:
public class MyFilter extends ZuulFilter {
@Override
String filterType() {
return "pre"; //定义filter的类型,有pre、route、post、error四种
}
@Override
int filterOrder() {
return 10; //定义filter的顺序,数字越小表示顺序越高,越先执行
}
@Override
boolean shouldFilter() {
return true; //表示是否需要执行该filter,true表示执行,false表示不执行
}
@Override
Object run() {
return null; //filter需要执行的具体操作
}
}
3.2 Zuul如何实现限流熔断?
实现一个过滤器,run()方法为空。
3.3 Zuul如何进行压力测试
实现一个压力测试的过滤器,路由指定到某个服务。
4. Zuul2.0概述
Zuul 1.0是基于 Servlet 来实现的,是一种 BIO 网络通信模式。
这种模式优点是:模型简单、开发调试运维简单。
缺点是:线程上下文切换会有开销,并且存在连接数限制。
基于1.0版本,Zuul 推出了 2.0版本,在网络通信模型进行了优化(还有其他)。
采用 AIO 的模式,相比于1.0版本,在线程开销上减小了,也能够接入更多的连接数。
但是缺点是:编程模型变复杂的同时也提高了开发运复杂度,并且更重要的一点是基于 ThreadLocal 实现的 RequestContext 无法工作了。
对比2个版本,2.0的升级带来了20%的性能提升,Zuul 1.0可以承受百亿级别的流量,2.0则达到千亿级。相对而言,2.0并没有带来很大的性能提升,并且1.0版本已经可以满足大多业务需要,此外也可以通过使用 AsyncServlet 对1.0进行优化。因此选择1.0版本会更合适些。
参考资料
- https://github.com/Netflix/zuul/wiki/How-We-Use-Zuul-At-Netflix
- 杨波极客专栏《微服务架构实战160讲》
- http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html