Zuul源码阅读
Motivation
说一下,为什么要阅读Netflix Zuul,最近在看alibaba/Sentinel 在网关方面的应用。发现网关的设计模式有很多通用的地方。Netflix Zuul 对比Zuul2 更能直接的看出网关的主要功能和核心设计,易于上手,所以选择这个。同时也再次实践一下怎么高效阅读源代码。
Design and Function
Zuul 的主要功能,包括,鉴权,路由,流量监控,负载,实时响应(动态配置和开关),错误处理。
这张图就是根据这些功能提出的设计,通过Filter 将整个过程连接起来,
可扩展性:
- 在不同阶段做不同的处理,做隔离。
- 通过groovy 脚本加载来实现动态加载新的filter。
ZuulFilter
下面我们就来看总重要的 ZuulFilter 的设计吧,首先是类结构图
先看最基本的IZuulFilter
接口, 定义了两个方法,这个filter 要不要执行,怎么执行。
public interface IZuulFilter {
/**
* a "true" return from this method means that the run() method should be invoked
*
* @return true if the run() method should be invoked. false will not invoke the run() method
*/
boolean shouldFilter();
/**
* if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
*
* @return Some arbitrary artifact may be returned. Current implementation ignores it.
* @throws ZuulException if an error occurs during execution.
*/
Object run() throws ZuulException;
}
这里暂时提出疑问,throw ZuulException 会怎样?,返回任意result 怎么处理。
下面来看 Abstratct ZuulFilter类:
/**
* Base abstract class for ZuulFilters. The base class defines abstract methods to define:
* filterType() - to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
* <p/>
* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not
* important for a filter. filterOrders do not need to be sequential.
* <p/>
* ZuulFilters may be disabled using Archius Properties.
* <p/>
* By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false
*
* @author Mikey Cohen
* Date: 10/26/11
* Time: 4:29 PM
*/
public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
private final AtomicReference<DynamicBooleanProperty> filterDisabledRef = new AtomicReference<>();
/**
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
* We also support a "static" type for static responses see StaticResponseFilter.
* Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
*
* @return A String representing that type
*/
abstract public String filterType();
/**
* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not
* important for a filter. filterOrders do not need to be sequential.
*
* @return the int order of a filter
*/
abstract public int filterOrder();
public int compareTo(ZuulFilter filter) {
return Integer.compare(this.filterOrder(), filter.filterOrder());
}
}
根据设计图,我们知道Filter有三个阶段 分别是 pre, route,post, 这里的filterType 就是指定filter type(不同type的执行顺序不一样)。filterOrder 方法就是指定相同type 下filter的执行顺序了。同时也是实现 Comparable接口的原因了。
关于自定义 type,我们稍后再看。
那我们来看看 这些type的顺序是怎么指定的,
/**
* Zuul Servlet filter to run Zuul within a Servlet Filter. The filter invokes pre-routing filters first,
* then routing filters, then post routing filters. Handled exceptions in pre-routing and routing
* call the error filters, then call post-routing filters. Errors in post-routing only invoke the error filters.
* Unhandled exceptions only invoke the error filters
*
* @author Mikey Cohen
* Date: 10/12/11
* Time: 2:54 PM
*/
public class ZuulServletFilter implements Filter {
private ZuulRunner zuulRunner;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
String bufferReqsStr = filterConfig.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
zuulRunner = new ZuulRunner(bufferReqs);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
try {
preRouting();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
try {
postRouting();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
}
现在 看到了,ZuulFilter是通过实现在Servlet Filter的生命周期中实现的。那现在我们可以猜想 preRouting() 里会把所有的pre Type 按顺序执行:
/**
* runs all "pre" filters. These filters are run before routing to the orgin.
*
* @throws ZuulException
*/
public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}
/**
* runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
*
* @param sType the filterType.
* @return
* @throws Throwable throws up an arbitrary exception
*/
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;
}
从代码可以看出process根据列表里的filter顺序执行,顺序在返回前就按order 排序了,这样,当一个 preFilter 执行抛出 ZuulException 之后,pre阶段就结束了,进到最初ServletFilter的后续阶段。
所以到这里为止,我们就知道Zuul是怎么工作和扩展的了,大家就可以根据自己的需要进行扩展了。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
HttpServletRequest & HttpServletResponse 中 Body 的获取
获取 HttpServletRequest 中的请求体 HttpServletRequest#getInputStream() 获取到请求的输入流,从该输入流中可以读取到请求体。不过这个流在被我们的代码 read 过后,之后的代码就会报错,因为流已经被我们读取过了 , 尝试使用 mark() , reset() 也是不行的,会抛出异常。可以通过将 HttpServletRequest 对象包装一层的方式来实现这个功能。 package org.hepeng.commons.http; import lombok.AllArgsConstructor; import lombok.Data; import org.apache.commons.io.IOUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Http...
-
下一篇
Spring MVC之DispatcherServlet请求处理详解
DispatcherServlet作为Spring用于处理web请求注册的唯一一个Servlet,所有的请求都是经由DispatcherServlet进行分发处理的。本文主要讲解DispatcherServlet是如何对请求进行分发,处理,并且生成相应的视图的。 1. 整体结构 在HttpServlet中,其对不同方式的请求进行了分发,比如对于GET请求,其提供了doGet()方法,对于POST请求,其提供了doPost()方法等等。通过这种方式,子类可以针对于当前请求的方式实现不同的方法即可。但是在DispatcherServlet中,由于需要使用同一的方式对不同的请求进行处理,因而其对各个请求方式进行了整合,如下就是DispatcherServlet针对GET和POST请求所编写的同一处理逻辑: @Override protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Docker容器配置,解决镜像无法拉取问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- MySQL数据库在高并发下的优化方案
- Dcoker安装(在线仓库),最新的服务器搭配容器使用