1.zuul是什么
Zuul 是 Netflix 开源的微服务网关,可以将非服务核心功能移至到网关中,比如:鉴权,请求日志等
2.zuul1系统架构图
请求经过ZuulServlet,然后执行各种类型的过滤器链处理完毕之后,将处理后的结果响应出去。而且Zuul强化了其过滤器功能,支持热加载过滤器。
- 源码解析
此次源码分析的版本是1.x,如果你看的是2.0版本以上的请忽略本文章。
3.1 servlet源码解析
我们知道一个web项目少不了一个servlet,它也是服务处理的入口和出口。所以先从ZuulServlet开始介绍。
public class ZuulServlet extends HttpServlet {
private static final long serialVersionUID = -3374242278843351500L;
// zuul执行器,ZuulServlet直接访问这个类的方法
private ZuulRunner zuulRunner;
/**
* 初始化ZuulRunner
* @param config
* @throws ServletException
*/
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 可以在servletConfig的配置中添加buffer-requests参数,表示缓存请求
String bufferReqsStr = config.getInitParameter("buffer-requests");
boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
// 创建ZuulRunner对象
zuulRunner = new ZuulRunner(bufferReqs);
}
/**
* 处理请求的核心方法
* @param servletRequest 请求对象
* @param servletResponse 响应对象
* @throws ServletException
* @throws IOException
*/
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
try {
// 包装http请求和响应对象
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
// 请求上下文对象
RequestContext context = RequestContext.getCurrentContext();
// 表示zuul已经在执行
context.setZuulEngineRan();
try {
// 执行路由的前置过滤器
preRoute();
} catch (ZuulException e) {
// 如果执行出错,先执行错误处理,再执行后置过滤器
error(e);
postRoute();
return;
}
try {
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
postRoute();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
/**
* executes "post" ZuulFilters
*
* @throws ZuulException
*/
void postRoute() throws ZuulException {
zuulRunner.postRoute();
}
/**
* executes "route" filters
*
* @throws ZuulException
*/
void route() throws ZuulException {
zuulRunner.route();
}
/**
* executes "pre" filters
*
* @throws ZuulException
*/
void preRoute() throws ZuulException {
zuulRunner.preRoute();
}
/**
* 初始化请求对象
*
* @param servletRequest
* @param servletResponse
*/
void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
zuulRunner.init(servletRequest, servletResponse);
}
/**
* error过滤器
*
* @param e
*/
void error(ZuulException e) {
// 设置异常信息
RequestContext.getCurrentContext().setThrowable(e);
zuulRunner.error();
}
3.1.1 处理流程
通过service方法,可以看出整个servlet的处理流程:
pre异常: pre -> error -> post
route异常: pre -> route -> error -> post
post异常: pre -> route -> post -> error
正常: pre -> route -> post
[说明]
pre:表示路由的前置过滤器链,route:表示路由的过滤器链,post:表示路由的后置过滤器链,error:表示路由错误过滤器链。
由此可见,责任链模式是zuul的核心(如果对这个设计模式不清楚的同学,可以参考:责任链模式)
3.1.2 各种过滤器类型解析
由于pre,post,route,error的最终处理逻辑都是一样的,所以只介绍pre过滤器。
上面调用preRoute方法时,最终会调用到ZuulRunner的preRoute方法。
ZuulServlet.java
void preRoute() throws ZuulException {
zuulRunner.preRoute();
}
而Zuul执行preRoute时,又是调用FilterProcessor的方法。由于FilterProcessor创建多个实例是没有任何意义的,所以使用了单例模式(参考:单例模式)
ZuulRunner.java
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
FilterProcessor中又调用了自身的runFilters方法
FilterProcessor.java
public void preRoute() throws ZuulException {
try {
runFilters("pre");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
// 其他异常转换为ZuulException异常
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
}
}
/**
*
* 运行某种类型的所有过滤器
*
* @param sType 过滤器类型:pre,route,post,error
* @return
* @throws Throwable throws up an arbitrary exception
*/
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
// 如果开启了路由的请求日志 ,将日志添加到RequestContext对象中
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
// 【1】
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
// 【2】
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
// 如果结果是布尔类型
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
通过上面的逻辑可以知道,过滤器运行的步骤:
(1) 添加路由日志
(2) 根据过滤器的优先级排序整个过滤器链
(3) 依次执行过滤器,如果是布尔类型汇总结果
关键位置分析:
【1】调用FilterLoader(获取改对象也是使用单例模式)的getFiltersByType方法
/**
* 返回指定的过滤器类型的过滤器列表(已经排序)
*
* @param filterType 过滤器类型
* @return a List<ZuulFilter>
*/
public List<ZuulFilter> getFiltersByType(String filterType) {
// 检查缓存
List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;
list = new ArrayList<ZuulFilter>();
// 通过注册器找到所有的过滤器(所以如果要添加过滤器需要添加到注册器中),由于注册器的代码比较简单,所以这里不做介绍
Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
// 查找指定类型的过滤器
for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
ZuulFilter filter = iterator.next();
if (filter.filterType().equals(filterType)) {
// 类型相同
list.add(filter);
}
}
// 根据filterOrder排序
Collections.sort(list); // sort by priority
// 添加到缓存中
hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
【2】FilterProcessor.processZuulFilter方法介绍
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
boolean bDebug = ctx.debugRouting();
final String metricPrefix = "zuul.filter-";
long execTime = 0;
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();
// context中添加过滤器的执行结果
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");
// 用于记录过滤器是否添加了新的key到requestContext对象中
Debug.compareContextState(filterName, copy);
}
break;
default:
break;
}
// 抛出异常
if (t != null) throw t;
// 过滤器结果通知
usageNotifier.notify(filter, s);
return o;
} catch (Throwable e) {
if (bDebug) {
Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage());
}
// 过滤器结果通知
usageNotifier.notify(filter, ExecutionStatus.FAILED);
if (e instanceof ZuulException) {
// ZuulException
throw (ZuulException) e;
} else {
// 转换为ZuulException
ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
// 添加过滤器执行结果
ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
throw ex;
}
}
}
执行FilterProcessor.processZuulFilter的执行步骤:
(1) 记录requestContext的快照
(2) 执行真正执行过滤器
(3) 记录过滤器结果和状态,如果是执行成功,还记录requestContext增加的key
(4) 通知过滤器执行状态
现在来分析一下真正执行过滤器的逻辑,即ZuulFilter.runFilter:
ZuulFilter.java
public ZuulFilterResult runFilter() {
// 记录过滤器结果
ZuulFilterResult zr = new ZuulFilterResult();
if (!isFilterDisabled()) {
// 过滤器有效
if (shouldFilter()) {
// 应该执行该过滤器
// 开始记录轨迹
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
// 运行过滤器并返回结果(新增的过滤器一定要重写这个方法)
Object res = run();
// 封装成Zuul过滤器结果对象(成功)
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable e) {
// 设置轨迹名称
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
// 封装成Zuul过滤器结果对象(异常)
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(e);
} finally {
// 停止轨迹记录
t.stopAndLog();
}
} else {
// 如果不执行就表示跳过
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}