Zuul 1代码阅读

237 阅读3分钟

1. 基础

Zuul的过滤器有4中标准的类型:

  1. pre:在请求被转发之前执行,可做权限检查、限流
  2. route:过滤器可以处理实际的请求转发
  3. post:在请求被转发之后执行,可做结果统计、response结果修改
  4. 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进行处理

  1. 先获得这个类型的全部filter
  2. 对每个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

  1. 判断是否disable
  2. 调用shouldFilter是否进行filter
  3. 调用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初始化

  1. DiscoveryClientRouteLocator
  2. PreDecorationFilter
  3. RibbonRoutingFilter
  4. SimpleHostRoutingFilter
  5. ServiceRouteMapper

ZuulServerAutoConfiguration继承自ZuulServerAutoConfiguration,在ZuulServerAutoConfiguration还会做如下初始化

  1. ZuulController <关键>
  2. CompositeRouteLocator
  3. SimpleRouteLocator
  4. 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
}

总结

  1. 可以看到zuul 1是基于servlet开发的,代码比较易读。
  2. 因为基于serlvet,处理流程较长,性能不是很好,作为高性能API网关有明显劣势。
  3. Zuul2 已经改为基于Netty进行开发,性能高很多。