zuul源码解析
此次源码分析的版本是1.x,如果你看的是2.0版本以上的请忽略本文章。
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();
}
-
处理流程
通过service方法,可以看出整个servlet的处理流程:
pre异常: pre -> error -> post
route异常: pre -> route -> error -> post
post异常: pre -> route -> post -> error
正常: pre -> route -> post
[说明]
pre:表示路由的前置过滤器链,route:表示路由的过滤器链,post:表示路由的后置过滤器链,error:表示路由错误过滤器链。
由此可见,责任链模式是zuul的核心 -
各种过滤器类型解析
由于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;
}
2.分析主要的几个过滤器
1.PreDecorationFilter
PreDecorationFilter.java
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
//获取到uri
final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest());
//匹配路由
Route route = this.routeLocator.getMatchingRoute(requestURI);
//如果匹配到路由 往ctx里面塞一些属性,如serviceId
//如果没匹配到会走SimpleHostRoutingFilter进行处理
if (route != null) {
String location = route.getLocation();
if (location != null) {
ctx.put(REQUEST_URI_KEY, route.getPath());
ctx.put(PROXY_KEY, route.getId());
if (!route.isCustomSensitiveHeaders()) {
this.proxyRequestHelper
.addIgnoredHeaders(this.properties.getSensitiveHeaders().toArray(new String[0]));
}
else {
this.proxyRequestHelper.addIgnoredHeaders(route.getSensitiveHeaders().toArray(new String[0]));
}
if (route.getRetryable() != null) {
ctx.put(RETRYABLE_KEY, route.getRetryable());
}
if (location.startsWith(HTTP_SCHEME+":") || location.startsWith(HTTPS_SCHEME+":")) {
ctx.setRouteHost(getUrl(location));
ctx.addOriginResponseHeader(SERVICE_HEADER, location);
}
else if (location.startsWith(FORWARD_LOCATION_PREFIX)) {
ctx.set(FORWARD_TO_KEY,
StringUtils.cleanPath(location.substring(FORWARD_LOCATION_PREFIX.length()) + route.getPath()));
ctx.setRouteHost(null);
return null;
}
else {
// set serviceId for use in filters.route.RibbonRequest
ctx.set(SERVICE_ID_KEY, location);
ctx.setRouteHost(null);
ctx.addOriginResponseHeader(SERVICE_ID_HEADER, location);
}
if (this.properties.isAddProxyHeaders()) {
addProxyHeaders(ctx, route);
String xforwardedfor = ctx.getRequest().getHeader(X_FORWARDED_FOR_HEADER);
String remoteAddr = ctx.getRequest().getRemoteAddr();
if (xforwardedfor == null) {
xforwardedfor = remoteAddr;
}
else if (!xforwardedfor.contains(remoteAddr)) { // Prevent duplicates
xforwardedfor += ", " + remoteAddr;
}
ctx.addZuulRequestHeader(X_FORWARDED_FOR_HEADER, xforwardedfor);
}
if (this.properties.isAddHostHeader()) {
ctx.addZuulRequestHeader(HttpHeaders.HOST, toHostHeader(ctx.getRequest()));
}
}
}
else {
log.warn("No route found for uri: " + requestURI);
String fallBackUri = requestURI;
String fallbackPrefix = this.dispatcherServletPath; // default fallback
// servlet is
// DispatcherServlet
if (RequestUtils.isZuulServletRequest()) {
// remove the Zuul servletPath from the requestUri
log.debug("zuulServletPath=" + this.properties.getServletPath());
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
log.debug("Replaced Zuul servlet path:" + fallBackUri);
}
else {
// remove the DispatcherServlet servletPath from the requestUri
log.debug("dispatcherServletPath=" + this.dispatcherServletPath);
fallBackUri = fallBackUri.replaceFirst(this.dispatcherServletPath, "");
log.debug("Replaced DispatcherServlet servlet path:" + fallBackUri);
}
if (!fallBackUri.startsWith("/")) {
fallBackUri = "/" + fallBackUri;
}
String forwardURI = fallbackPrefix + fallBackUri;
forwardURI = DOUBLE_SLASH.matcher(forwardURI).replaceAll("/");
ctx.set(FORWARD_TO_KEY, forwardURI);
}
return null;
}
2.RibbonRoutingFilter
最主要的一个过滤器,负载均衡,请求转发都在这里干的,下面着重分析
RibbonRoutingFilter.java
//进行路由转发,负载均衡
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
//命令模式
//对将要发送的请求进行一个封装 一些参数请求url之类的
RibbonCommandContext commandContext = buildCommandContext(context);
//发送请求
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
//这里返回一个具体的RibbonCommand的实现
//基于你的依赖,这里我走的方法是OkHttpRibbonCommandFactory 见2.1
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
//执行命令com.netflix.hystrix.HystrixCommand#execute 见2.2之后的解析
//这个方法最终会走到AbstractRibbonCommand的run方法
ClientHttpResponse response = command.execute();
this.helper.appendDebug(info, response.getRawStatusCode(), response.getHeaders());
//返回响应结果 到这里调用下游服务结束
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
}
}
2.1OkHttpRibbonCommandFactory
OkHttpRibbonCommandFactory.java
@Override
public OkHttpRibbonCommand create(final RibbonCommandContext context) {
//获取服务id
final String serviceId = context.getServiceId();
//获取降级类
//我们在项目中使用Spring cloud zuul的时候,有一种这样的需求,就是当我们的zuul进行路由分发时,如 果后端服务没有启动,或者调用超时,这时候我们希望Zuul提供一种降级功能,而不是将异常暴露出来。
FallbackProvider fallbackProvider = getFallbackProvider(serviceId);
//获取负载均衡客户端
//这里面会设置负载均衡rule,可以自定义rule
final OkHttpLoadBalancingClient client = this.clientFactory.getClient(
serviceId, OkHttpLoadBalancingClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new OkHttpRibbonCommand(serviceId, client, context, zuulProperties, fallbackProvider,
clientFactory.getClientConfig(serviceId));
}
2.2HystrixCommand#execute
HystrixCommand.java
public R execute() {
try {
//主要看queue()方法
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
public Future<R> queue() {
/*
* The Future returned by Observable.toBlocking().toFuture() does not implement the
* interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;
* thus, to comply with the contract of Future, we must wrap around it.
*/
//看toObservable(),这里用的响应式编程比较难看
final Future<R> delegate = toObservable().toBlocking().toFuture();
final Future<R> f = new Future<R>() {
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (delegate.isCancelled()) {
return false;
}
if (HystrixCommand.this.getProperties().executionIsolationThreadInterruptOnFutureCancel().get()) {
/*
* The only valid transition here is false -> true. If there are two futures, say f1 and f2, created by this command
* (which is super-weird, but has never been prohibited), and calls to f1.cancel(true) and to f2.cancel(false) are
* issued by different threads, it's unclear about what value would be used by the time mayInterruptOnCancel is checked.
* The most consistent way to deal with this scenario is to say that if *any* cancellation is invoked with interruption,
* than that interruption request cannot be taken back.
*/
interruptOnFutureCancel.compareAndSet(false, mayInterruptIfRunning);
}
final boolean res = delegate.cancel(interruptOnFutureCancel.get());
if (!isExecutionComplete() && interruptOnFutureCancel.get()) {
final Thread t = executionThread.get();
if (t != null && !t.equals(Thread.currentThread())) {
t.interrupt();
}
}
return res;
}
@Override
public boolean isCancelled() {
return delegate.isCancelled();
}
@Override
public boolean isDone() {
return delegate.isDone();
}
@Override
public R get() throws InterruptedException, ExecutionException {
return delegate.get();
}
@Override
public R get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return delegate.get(timeout, unit);
}
};
/* special handling of error states that throw immediately */
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception e) {
Throwable t = decomposeException(e);
if (t instanceof HystrixBadRequestException) {
return f;
} else if (t instanceof HystrixRuntimeException) {
HystrixRuntimeException hre = (HystrixRuntimeException) t;
switch (hre.getFailureType()) {
case COMMAND_EXCEPTION:
case TIMEOUT:
// we don't throw these types from queue() only from queue().get() as they are execution errors
return f;
default:
// these are errors we throw from queue() as they as rejection type errors
throw hre;
}
} else {
throw Exceptions.sneakyThrow(t);
}
}
}
return f;
}
2.提示
-
toObservable()这个方法里面全是响应式编程,需要看懂需要学习RxJava
-
com.netflix.hystrix.HystrixCommand#getExecutionObservable 走到了负载均衡选择的方法
-
后面的源码解析在Zuul源码解析2