1. 基础
Zuul的过滤器有4中标准的类型:
- pre:在请求被转发之前执行,可做权限检查、限流
- route:过滤器可以处理实际的请求转发
- post:在请求被转发之后执行,可做结果统计、response结果修改
- error:在请求被转发时发生错误,执行过滤器,只处理zuul本身的异常,内部服务的异常默认不处理,除非检查后再封装抛出。
2. 代码
注意:以下代码基于版本
zuul-core 1.3.1
spring-cloud Greenwich.RC2
Zuul入口
com.netflix.zuul.http.ZuulServlet
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
可以看到顺序是preRoute > route > postRoute ,任何一步出现异常,进入errorRoute。
runFilters
preRoute、route、postRoute都是调用runFilters进行处理
- 先获得这个类型的全部filter
- 对每个filter调用processZuulFilter,实际会出发filter的run方法,获得结果 任何一步返回false并不会对同级别filter产生影响,任何一步为true,结果即为true
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 = (ZuulFilter)list.get(i);
Object result = this.processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= (Boolean)result;
}
}
}
return bResult;
}
getFiltersByType
根据filterType获取filter list,并对filter基于filterType从小到大排序
public List<ZuulFilter> getFiltersByType(String filterType) {
List<ZuulFilter> list = (List)this.hashFiltersByType.get(filterType);
if (list != null) {
return list;
} else {
List<ZuulFilter> list = new ArrayList();
Collection<ZuulFilter> filters = this.filterRegistry.getAllFilters();
Iterator iterator = filters.iterator();
while(iterator.hasNext()) {
ZuulFilter filter = (ZuulFilter)iterator.next();
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
Collections.sort(list); //排序
this.hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
}
runFilter
- 判断是否disable
- 调用shouldFilter是否进行filter
- 调用run方法进行filter处理
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
if (!this.isFilterDisabled()) {
if (this.shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
Object res = this.run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable var7) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(var7);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
在pre filter时如何停止后续访问
RequestContext.getCurrentContext().setSendZuulResponse(false); // 过滤该请求,不对其进行路由 netflix-zuul的RouteFilter会对这个变量进行检查,如果不为true就不会进行route了。 org.springframework.cloud.netflix.zuul.filters.route.SimpleHostRoutingFilter
public boolean shouldFilter() {
return RequestContext.getCurrentContext().getRouteHost() != null
&& RequestContext.getCurrentContext().sendZuulResponse();
}
org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter 也有这个检查。
3. Zuul启动原理
启用Zuul只需要在Application上加上注册EnableZuulProxy即可
@EnableZuulProxy
这个注解会启动一个zuul server并启用反向代理,如果不启用发现代理使用EnableZuulServer注解。
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
EnableZuulProxy Import了ZuulServerMarkerConfiguration,这个会触发ZuulServerAutoConfiguration。 在ZuulServerAutoConfiguration里面会进行如下bean初始化
- DiscoveryClientRouteLocator
- PreDecorationFilter
- RibbonRoutingFilter
- SimpleHostRoutingFilter
- ServiceRouteMapper
ZuulServerAutoConfiguration继承自ZuulServerAutoConfiguration,在ZuulServerAutoConfiguration还会做如下初始化
- ZuulController <关键>
- CompositeRouteLocator
- SimpleRouteLocator
- ZuulHandlerMapping
ZuulController是处理所有请求的入口,它注册ZuulServlet作为Servlet类,到达zuul server的请求都被ZuulServlet处理。
public class ZuulController extends ServletWrappingController {
public ZuulController() {
setServletClass(ZuulServlet.class);
setServletName("zuul");
setSupportedMethods((String[]) null); // Allow all
}
//删除部分code
}
总结
- 可以看到zuul 1是基于servlet开发的,代码比较易读。
- 因为基于serlvet,处理流程较长,性能不是很好,作为高性能API网关有明显劣势。
- Zuul2 已经改为基于Netty进行开发,性能高很多。