美文网首页SpringBoot精选
SpringCloud-源码分析 zuul (二)

SpringCloud-源码分析 zuul (二)

作者: 叩丁狼教育 | 来源:发表于2019-01-04 10:28 被阅读70次

    本文作者:陈刚,叩丁狼高级讲师。原创文章,转载请注明出处。

    在上一章节我们分析了Zuul中的各种filter,那这一章我们来跟踪一下zuul的执行流程。那么入口肯定是我们的 ZuulServlet ,他类似于 DispatcherServlet 在请求的最前面做分发。我们来看一下他的源码

    /**
     * Core Zuul servlet which intializes and orchestrates zuulFilter execution
     *
     * @author Mikey Cohen
     *         Date: 12/23/11
     *         Time: 10:44 AM
     */
    public class ZuulServlet extends HttpServlet {
    
        private static final long serialVersionUID = -3374242278843351500L;
        private ZuulRunner zuulRunner;
    
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            super.init(config);
    
            String bufferReqsStr = config.getInitParameter("buffer-requests");
            boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
    
            zuulRunner = new ZuulRunner(bufferReqs);
        }
    
        @Override
        public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
            try {
                init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
    
                // Marks this request as having passed through the "Zuul engine", as opposed to servlets
                // explicitly bound in web.xml, for which requests will not have the same data attached
                RequestContext context = RequestContext.getCurrentContext();
                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();
        }
    
        /**
         * initializes request
         *
         * @param servletRequest
         * @param servletResponse
         */
        void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
            zuulRunner.init(servletRequest, servletResponse);
        }
    
        /**
         * sets error context info and executes "error" filters
         *
         * @param e
         */
        void error(ZuulException e) {
            RequestContext.getCurrentContext().setThrowable(e);
            zuulRunner.error();
        }
    
    

    翻译类上的注释“Core Zuul servlet which intializes and orchestrates zuulFilter execution” , 他是最核心的servlet,负责调用各种filter去执行,
    在init初始化方法中获取并使用 buffer-requests 配置来创建了ZuulRunner 对象。
    在service方法中在依次调用 preRoute ,route ,postRoute ,如果出现异常会调用 error , 这这些方法最终都会去触发 zuulRunner中的各种路由方法。我们看一下ZuulRunner的源码

    
    /**
     * This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls
     * to preRoute(), route(),  postRoute(), and error() methods
     *
     * @author mikey@netflix.com
     * @version 1.0
     */
    public class ZuulRunner {
    
        private boolean bufferRequests;
    
        /**
         * Creates a new <code>ZuulRunner</code> instance.
         */
        public ZuulRunner() {
            this.bufferRequests = true;
        }
    
        /**
         *
         * @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body.
    在ZuulServlet中创建ZuulRunner的时候传入的配置参数 bufferRequests, 它决定了是否把 ServletRequest包装在HttpServletRequestWrapper中并缓冲主体。
         */
        public ZuulRunner(boolean bufferRequests) {
            this.bufferRequests = bufferRequests;
        }
    
        /**
         * sets HttpServlet request and HttpResponse
         设置 请求对象和响应对象到  RequestContext 中
         * @param servletRequest
         * @param servletResponse
         */
        public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
    
            RequestContext ctx = RequestContext.getCurrentContext();
            if (bufferRequests) {
    //把 ServletRequest包装在HttpServletRequestWrapper中并缓冲主体。
                ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
            } else {
                ctx.setRequest(servletRequest);
            }
    
            ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
        }
    
    //这里通过 FilterProcessor 执行器调用各种 ZuulFilters
        /**
         * executes "post" filterType  ZuulFilters
         *
         * @throws ZuulException
         */
        public void postRoute() throws ZuulException {
            FilterProcessor.getInstance().postRoute();
        }
    
        /**
         * executes "route" filterType  ZuulFilters
         *
         * @throws ZuulException
         */
        public void route() throws ZuulException {
            FilterProcessor.getInstance().route();
        }
    
        /**
         * executes "pre" filterType  ZuulFilters
         *
         * @throws ZuulException
         */
        public void preRoute() throws ZuulException {
            FilterProcessor.getInstance().preRoute();
        }
    
        /**
         * executes "error" filterType  ZuulFilters
         */
        public void error() {
            FilterProcessor.getInstance().error();
        }
    

    从名字上我们能知道 ZuulRunner就是Zuul的执行器 ,而Zuul中是通过各种filter来完成的,那么ZuulRunner就是负责去调用各种 filter 。
    从他的注释上亦可得知 "This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls to preRoute(), route(), postRoute(), and error() methods"大致意思是此类将servlet请求和响应初始化为RequestContext,并使用FilterProcessor执行器去调用preRoute(),route(),postRoute()和error()方法,我们可以从这几个方法中电源得到证实。

    我们跟踪一下 FilterProcessor的源码看一下他是如何执行的

    public class FilterProcessor {
      ...省略代码...
      /**
         * runs "post" filters which are called after "route" filters. ZuulExceptions from ZuulFilters are thrown.
         * Any other Throwables are caught and a ZuulException is thrown out with a 500 status code
         *
         * @throws ZuulException
         */
        public void postRoute() throws ZuulException {
            try {
                runFilters("post");
            } catch (ZuulException e) {
                throw e;
            } catch (Throwable e) {
                throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
            }
        }
    
        /**
         * runs all "error" filters. These are called only if an exception occurs. Exceptions from this are swallowed and logged so as not to bubble up.
         */
        public void error() {
            try {
                runFilters("error");
            } catch (Throwable e) {
                logger.error(e.getMessage(), e);
            }
        }
    
        /**
         * Runs all "route" filters. These filters route calls to an origin.
         *
         * @throws ZuulException if an exception occurs.
         */
        public void route() throws ZuulException {
            try {
                runFilters("route");
            } catch (ZuulException e) {
                throw e;
            } catch (Throwable e) {
                throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
            }
        }
    
        /**
         * runs all "pre" filters. These filters are run before routing to the orgin.
         *
         * @throws ZuulException
         */
        public void preRoute() throws ZuulException {
            try {
                runFilters("pre");
            } catch (ZuulException e) {
                throw e;
            } catch (Throwable e) {
                throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
            }
        }
    
        /**
         * runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
         *
         * @param sType the filterType.
         * @return
         * @throws Throwable throws up an arbitrary exception
         */
        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 = list.get(i);
                    Object result = processZuulFilter(zuulFilter);
                    if (result != null && result instanceof Boolean) {
                        bResult |= ((Boolean) result);
                    }
                }
            }
            return bResult;
        }
    
        /**
         * Processes an individual ZuulFilter. This method adds Debug information. Any uncaught Thowables are caught by this method and converted to a ZuulException with a 500 status code.
         *
         * @param filter
         * @return the return value for that filter
         * @throws ZuulException
         */
        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();
                }
                //真正执行ZuulFilter内存方法
                ZuulFilterResult result = filter.runFilter();
                ExecutionStatus s = result.getStatus();
                execTime = System.currentTimeMillis() - ltime;
    
                switch (s) {
                    case FAILED:
                        t = result.getException();
                        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");
                            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) {
                    throw (ZuulException) e;
                } else {
                    ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
                    ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                    throw ex;
                }
            }
        }
    ...省略代码...
    

    我们先看 preRoute , 他调用了 runFilters("pre")方法 ,"pre" 很明显代表了filter的类型为前置过滤器,所有的“pre”过滤器调用都在这里面发起, 而 runFilters 中通过 FilterLoader.getInstance().getFiltersByType(sType); 获取了所有的“pre” 过滤器,然后遍历通过调用Object result = processZuulFilter(zuulFilter); 方法执行 并拿到结果 。

    而在processZuulFilter方法中通过执行 Filter 本身的 runFilter(); 方法完成调用 。

    public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
    ...省略...
     public ZuulFilterResult runFilter() {
            ZuulFilterResult zr = new ZuulFilterResult();
            if (!isFilterDisabled()) {
                if (shouldFilter()) {
                    Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
                    try {
                  //真正执行ZuulFilter内存方法
                        Object res = run();
                        zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                    } catch (Throwable e) {
                        t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                        zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                        zr.setException(e);
                    } finally {
                        t.stopAndLog();
                    }
                } else {
                    zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
                }
            }
            return zr;
        }
    

    这里最终调用了 IZuulFilter的run()方法 ,那其实最终就调用到了 IZuulFilter的各种实现类filter的run方法,完成业务处理 。

    到这里同学们只需要结合上一章节的每种filter的逻辑就能对zuul的执行流程有一个大致的理解了。

    想获取更多技术干货,请前往叩丁狼官网:http://www.wolfcode.cn/all_article.html

    相关文章

      网友评论

        本文标题:SpringCloud-源码分析 zuul (二)

        本文链接:https://www.haomeiwen.com/subject/abhhrqtx.html