十九.SpringCloud源码剖析-Zuul的执行流程

简介: 在之前的章节我们详细分析了Zuul的Filter,这一章节我们来详细跟一下Zuul的执行流程

前言

在之前的章节我们详细分析了Zuul的Filter,这一章节我们来详细跟一下Zuul的执行流程

<hr style=" border:solid; width:100px; height:1px;" color=#000000 size=1">

Zuul的执行流程是这样的

  • 首先请求进来会先到达ZuulController ,ZuulController把请求交给ZuulServlet去处理
  • 在ZuulServelt会调用 ZuulRunner 依次执行: init初始化,pre前置filter,route路由filter,post后置filter, error 异常filter
  • ZuulRunner通过 FilterProcessor 去执行各种Filter,FilterProcessor通过 FilterLoader 加载 各种filters
  • 执行完成之后,把结果响应给客户端

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

Zuul执行流程

1.ZuulController 请求入口

ZuulController 是请求的入口,把请求交给ZuulServlet去处理,ZuulServlet 类似于 DispatcherServlet 在请求的最前面做分发

public class ZuulController extends ServletWrappingController {
   
   

    public ZuulController() {
   
       
        //1.为父类的serveltClass 做初始化,是一个 ZuulServlet
        setServletClass(ZuulServlet.class);
        setServletName("zuul");
        setSupportedMethods((String[]) null); // Allow all
    }

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
   
        try {
   
   
            // We don't care about the other features of the base class, just want to
            // handle the request
            //2.请求交个父类
            return super.handleRequestInternal(request, response);
        }
        finally {
   
   
            // @see com.netflix.zuul.context.ContextLifecycleFilter.doFilter
            RequestContext.getCurrentContext().unset();
        }
    }

}

下面是它父类的源码

public class ServletWrappingController extends AbstractController implements BeanNameAware, InitializingBean, DisposableBean {
   
   
    @Nullable
    private Class<? extends Servlet> servletClass;
    @Nullable
    private String servletName;
    private Properties initParameters = new Properties();
    @Nullable
    private String beanName;
    //servletInstance 就是 ZuulServelt
    @Nullable
    private Servlet servletInstance;

       ...省略...
        public void afterPropertiesSet() throws Exception {
   
   
        if (this.servletClass == null) {
   
   
            throw new IllegalArgumentException("'servletClass' is required");
        } else {
   
   
            if (this.servletName == null) {
   
   
                this.servletName = this.beanName;
            }
            //1.反射,根据 servletClass 创建实例
            this.servletInstance = (Servlet)ReflectionUtils.accessibleConstructor(this.servletClass, new Class[0]).newInstance();
            this.servletInstance.init(new ServletWrappingController.DelegatingServletConfig());
        }
    }

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
   
   
        Assert.state(this.servletInstance != null, "No Servlet instance");
        //2.调用ZuulServelt的service方法
        this.servletInstance.service(request, response);
        return null;
    }

2.ZuulServlet 请求的分发

ZuulServelt会调用 ZuulRunner 依次执行: init初始化,pre前置filter,route路由filter,post后置filter, error 异常filter,ZuulServlet 源码如下

/**
 1.核心Zuul servlet,可初始化和协调zuulFilter执行
 * 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;

    //初始化 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 {
   
   
            //初始化 , 调用zuulRunner的init方法,
            //主要是把ServletRequest请求对象设置给RequestContext上下文
            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();
            //将此请求标记为“ Zuul引擎”,而不是servlet
            context.setZuulEngineRan();

            try {
   
   
                //执行pre的filter
                preRoute();
            } catch (ZuulException e) {
   
   
                //如果异常了会走 error的filter,然后再走post的filter
                error(e);
                postRoute();
                return;
            }
            try {
   
   
                //执行route的filter
                route();
            } catch (ZuulException e) {
   
   
               //如果异常了会走 error的filter,然后再走post的filter
                error(e);
                postRoute();
                return;
            }
            try {
   
   
                //执行post的filter
                postRoute();
            } catch (ZuulException e) {
   
   
               //如果异常了会走 error的filter,不会再走post的filter
                error(e);
                return;
            }

        } catch (Throwable e) {
   
   
            //如果异常了会走 error的filter,默认错误码500
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
   
   
            RequestContext.getCurrentContext().unset();
        }
    }

    /**
     * executes "post" ZuulFilters
     *
     * @throws ZuulException
     */
     //调用zuulRunner执行 post filter
    void postRoute() throws ZuulException {
   
   
        zuulRunner.postRoute();
    }

    /**
     * executes "route" filters
     *
     * @throws ZuulException
     */
     //调用zuulRunner执行 route filter
    void route() throws ZuulException {
   
   
        zuulRunner.route();
    }

    /**
     * executes "pre" filters
     *
     * @throws ZuulException
     */
     //调用zuulRunner执行 pre filter
    void preRoute() throws ZuulException {
   
   
        zuulRunner.preRoute();
    }

    /**
     * initializes request
     *
     * @param servletRequest
     * @param servletResponse
     */
     //调用zuulRunner执行 初始化 ,主要是把servletRequest设置给ReqestContext上下文对象
     void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
   
   
        zuulRunner.init(servletRequest, servletResponse);
    }

    /**
     * sets error context info and executes "error" filters
     *
     * @param e
     */
     //通过RequestContext设置异常
     //通过zuulRunner执行 error filter
    void error(ZuulException e) {
   
   
        RequestContext.getCurrentContext().setThrowable(e);
        zuulRunner.error();
    }

3.ZuulRunner 执行器

上面涉及到一个非常重要的类ZuulRunner所有的请求都是通过它来执行的,此类将Servlet请求和响应初始化到RequestContext中,并将FilterProcessor调用包装为preRoute(),route(),postRoute()和error()方法 ,我们看一下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();
    }

4.FilterProcessor filter执行器

继续跟踪一下 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 {
   
   
            //执行 post 类型的filter
            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 {
   
   
            //执行 error 类型的filter
            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 {
   
   
            //执行 post 类型的filter
            runFilters("post");
        } 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 {
   
   
            //执行 pre 类型的filter
            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
     */
     //执行Filter的核心方法,运行所有过滤器/在过滤器中使用此方法可按类型运行自定义过滤器
    public Object runFilters(String sType) throws Throwable {
   
   
        if (RequestContext.getCurrentContext().debugRouting()) {
   
   
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        //通过 FilterLoader 加载 filters
        List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
        if (list != null) {
   
   
            for (int i = 0; i < list.size(); i++) {
   
   
                ZuulFilter zuulFilter = list.get(i);
                //拿到每个filter,调用 processZuulFilter 去执行
                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
     */
     //filter的执行方法
    public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
   
   

        RequestContext ctx = RequestContext.getCurrentContext();
        //获取时候开启:debug
        boolean bDebug = ctx.debugRouting();
        final String metricPrefix = "zuul.filter-";
        long execTime = 0;
        String filterName = "";
        try {
   
   
            long ltime = System.currentTimeMillis();
            //filter的类名

            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.runFilter方法真正执行Filter
            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;
            }
        }
    }
...省略代码...

不管是前置,后置,路由,异常的Filter ,FilterProcessor 都通过 runFilters("类型"); 方法去执行filter,方法中通过 FilterLoader加载指定类型的Filter列表,然后交给 processZuulFilter方法去执行 ,该方法最终调用ZuulFilter.runFilter();方法去执行具体的Filter 。它会以下面的顺序去执行内置的Filter:
| 类型 | 过滤器 | 描述 | 顺序 |
| ----- | ----------------------- | ------------------------------------------------------------ | ---- |
| pre | ServletDetectionFilter | 在pre过滤器中,ServletDetectionFilter是第一个执行的过滤器,检测请求是用 DispatcherServlet还是 ZuulServlet,将结果设置到RequestContext中 | -3 |
| pre | Servlet30WrapperFilter | 主要是将原始请求进行包装,将原始的HttpServletRequest请求包装成Servlet30RequestWrapper类型 | -2 |
| pre | FormBodyWrapperFilter | 同Servlet30RequestWrapper一样,也是对请求的一个包装,只不过他只包装表单数据,即:content-type中必须带有“application/x-www-form-urlencoded”或“multipart/form-data” | -1 |
| error | SendErrorFilter | 这个是用来发送错误的Filter | 0 |
| pre | DebugFilter | 设置请求过程是否开启debug,将当前请求上下文中的debugRoutingdebugRequest参数设置为true | 1 |
| pre | PreDecorationFilter | 基本的路由转发配置,根据uri调用哪一个route过滤器 | 5 |
| route | RibbonRoutingFilter | 服务路由的过滤器,使用用Ribbon 做负载均衡,hystrix做熔断 | 10 |
| route | SimpleHostRoutingFilter | 简单主机路由过滤器,如果使用url路由,则用这个过滤器 | 100 |
| route | SendForwardFilter | 它使用RequestDispatcher转发请求 | 500 |
| post | SendResponseFilter | SendResponseFilter是Zuul的最后一个Filter,负责最终响应结果的输出。 | 1000 |

error类型的filter在出异常的时候才会执行

5.ZuulFilter

ZuulFilter 是所有Filter的抽象类,它的接口是IZuulFilter ,它里面有四个很重要的方法

//这个是
public interface IZuulFilter {
   
   
    //是否要执行Run方法
    boolean shouldFilter();
    //filter的核心业务方法
    Object run() throws ZuulException;
}

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
   
   

    private final AtomicReference<DynamicBooleanProperty> filterDisabledRef = new AtomicReference<>();

    /**
     * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
     * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
     * We also support a "static" type for static responses see  StaticResponseFilter.
     * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
     *
     * @return A String representing that type
     */
     //这个是filter的类型 ,有 pre ,route,post error
    abstract public String filterType();

    /**
     * filterOrder() must also be defined for a filter. Filters may have the same  filterOrder if precedence is not
     * important for a filter. filterOrders do not need to be sequential.
     *
     * @return the int order of a filter
     */
     //这个是filter的执行顺序,越小越先执行
    abstract public int filterOrder();
    ...省略...
}

详细看一下ZuulFilter 的源码

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
   
   
...省略...
 public ZuulFilterResult runFilter() {
   
   
        ZuulFilterResult zr = new ZuulFilterResult();
        if (!isFilterDisabled()) {
   
   
            //调用shouldFilter方法,这是个抽象方法,子类需要复写该方法返回 bool值决定要不要执行run方法
            if (shouldFilter()) {
   
   
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
                try {
   
   
                      //调用 filter的run方法去执行,也是个抽象方法,需要子类去实现自己的业务逻辑
                    Object res = run();
                    //执行成功,包装一个成功状态的ZuulFilterResult对象返回
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable e) {
   
   
                    t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                    //执行失败,包装一个失败状态的ZuulFilterResult对象返回
                    zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                    zr.setException(e);
                } finally {
   
   
                    t.stopAndLog();
                }
            } else {
   
   
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }
        return zr;
    }

ZuulFilter 是个抽象类,需要具体的子类类去复写其中的四个方法,然后 ZuulFilter 做的事情就依次调用 shouldFilter 方法,如果返回true就调用 run方法执行子类的业务逻辑,然后把结果封装成ZuulFilterResult返回。

到这里Zuul的执行流程的跟踪就算结束了,剩下的就是具体的Filter本身的业务代码的理解了,Zuul通过依次调用这个写Filter来完成整个生命周期的执行。最后总结一下流程

在这里插入图片描述

相关文章
|
19天前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
62 5
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
480 37
|
3月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
160 5
|
4月前
|
人工智能 前端开发 Java
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
本文介绍了如何使用 **Spring Cloud Alibaba AI** 构建基于 Spring Boot 和 uni-app 的聊天机器人应用。主要内容包括:Spring Cloud Alibaba AI 的概念与功能,使用前的准备工作(如 JDK 17+、Spring Boot 3.0+ 及通义 API-KEY),详细实操步骤(涵盖前后端开发工具、组件选择、功能分析及关键代码示例)。最终展示了如何成功实现具备基本聊天功能的 AI 应用,帮助读者快速搭建智能聊天系统并探索更多高级功能。
1533 2
【实操】Spring Cloud Alibaba AI,阿里AI这不得玩一下(含前后端源码)
|
2月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
53 0
|
4月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
4月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
4月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
|
4月前
|
SQL Java 数据库连接
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
|
4月前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known