zuul源码解析1

本文详细剖析了Zuul 1.x版本的核心组件ZuulServlet及其内部执行流程,包括预处理(pre)、路由处理(route)、后处理(post)及错误处理(error)等阶段,并深入探讨了关键过滤器PreDecorationFilter和RibbonRoutingFilter的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

zuul源码解析

此次源码分析的版本是1.x,如果你看的是2.0版本以上的请忽略本文章。

1.执行流程
  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();
    }
  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的核心

  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;
    }
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.提示
  1. toObservable()这个方法里面全是响应式编程,需要看懂需要学习RxJava

  2. com.netflix.hystrix.HystrixCommand#getExecutionObservable 走到了负载均衡选择的方法

  3. 后面的源码解析在Zuul源码解析2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值