zuul之請(qǐng)求處理流程

目錄

概述

zuul 是netflix開(kāi)源的一個(gè)API Gateway 服務(wù)器, 本質(zhì)上是一個(gè)web servlet應(yīng)用,下面通過(guò)源碼分析zuul的請(qǐng)求處理流程

Zuul請(qǐng)求的生命周期

如圖,該圖詳細(xì)描述了各種類型的過(guò)濾器的執(zhí)行順序

zuul.png

通過(guò)上圖可以看到zuul通過(guò)攔截servlet應(yīng)用http請(qǐng)求后進(jìn)入zuul定義的過(guò)濾器鏈表進(jìn)行處理路由;

一般我們開(kāi)發(fā)web應(yīng)用需要在請(qǐng)求進(jìn)來(lái)前進(jìn)行統(tǒng)一處理,通常有兩種方式

  1. 定義一個(gè)servlet Filter來(lái)在請(qǐng)求進(jìn)來(lái)前攔截處理,例如會(huì)話校驗(yàn),請(qǐng)求header校驗(yàn)等
  2. 定義一個(gè)統(tǒng)一servlet,所有請(qǐng)求進(jìn)來(lái)都走此servlet,一般mvc框架通過(guò)此方式實(shí)現(xiàn),例如springMvc的DispatcherServlet

zuul也不例外,zuul分別實(shí)現(xiàn)了這兩種攔截方式: ZuulServletFilter與ZuulServlet。

ZuulServletFilter

所在包:com.netflix.zuul.filters


public class ZuulServletFilter implements Filter {

    //zuul核心運(yùn)行器
    private ZuulRunner zuulRunner;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        String bufferReqsStr = filterConfig.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;

        //創(chuàng)建
        zuulRunner = new ZuulRunner(bufferReqs);
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {
            //初始化當(dāng)前請(qǐng)求zuul的上下問(wèn)環(huán)境信息
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
            //執(zhí)行前置過(guò)濾器列表
            try {
                preRouting();
            } catch (ZuulException e) {
                //執(zhí)行錯(cuò)誤過(guò)濾器列表
                //執(zhí)行后置路由過(guò)濾器列表
                error(e);
                postRouting();
                return;
            }
            
            //判斷響應(yīng)是否已經(jīng)發(fā)送,發(fā)送完成時(shí)不需要執(zhí)行下面路由列表流程
            // Only forward onto to the chain if a zuul response is not being sent
            if (!RequestContext.getCurrentContext().sendZuulResponse()) {
                filterChain.doFilter(servletRequest, servletResponse);
                return;
            }
            
            //執(zhí)行路由中過(guò)濾器列表
            try {
                routing();
            } catch (ZuulException e) {
                //執(zhí)行錯(cuò)誤過(guò)濾器列表
                //執(zhí)行后置路由過(guò)濾器列表
                error(e);
                postRouting();
                return;
            }
            //執(zhí)行后置路由過(guò)濾器列表
            try {
                postRouting();
            } catch (ZuulException e) {
                //執(zhí)行錯(cuò)誤過(guò)濾器列表
                error(e);
                return;
            }
        } catch (Throwable e) {
            //執(zhí)行錯(cuò)誤過(guò)濾器列表
            error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

    void postRouting() throws ZuulException {
        //zuul核心運(yùn)行器執(zhí)行后置過(guò)濾器列表
        zuulRunner.postRoute();
    }

    void routing() throws ZuulException {
        zuulRunner.route();
    }

    void preRouting() throws ZuulException {
        zuulRunner.preRoute();
    }

    void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        zuulRunner.init(servletRequest, servletResponse);
    }

    void error(ZuulException e) {
       //設(shè)置異常的上下文環(huán)境中
        RequestContext.getCurrentContext().setThrowable(e);
        zuulRunner.error();
    }
}

ZuulServlet

所在包:com.netflix.zuul.http

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();
    }
}

ZuulServlet與ZuulServletFilter完成了相同的功能

  1. 初始化時(shí)創(chuàng)建ZuulRunner
  2. 請(qǐng)求進(jìn)入調(diào)用ZuulRunner.init初始化當(dāng)前請(qǐng)求的上下文環(huán)境
  3. 按規(guī)則執(zhí)行zuul的過(guò)濾器列表:
  • 正常情況 :preRoute()->route()->postRoute()
  • preRoute異常:preRoute()->error()->postRoute()
  • route異常:route()->error()->postRoute()
  • postRoute異常:postRoute()->error()

ZuulRunner

ZuulRunner將servlet請(qǐng)求和響應(yīng)初始化到請(qǐng)求上下文中,并封裝過(guò)濾器處理器調(diào)用preRoute()、Router()、postRoute()和Error()方法

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.
     */
    public ZuulRunner(boolean bufferRequests) {
        this.bufferRequests = bufferRequests;
    }

    /**
     * sets HttpServlet request and HttpResponse
     *
     * @param servletRequest
     * @param servletResponse
     */
    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
        if (bufferRequests) {
            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
        } else {
            ctx.setRequest(servletRequest);
        }

        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
    }

    /**
     * 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 是提供給請(qǐng)求的核心處理類,主要完成下面事件

  1. 初始化request,response到上下問(wèn)環(huán)境
  2. 將 FilterProcessor的過(guò)濾器調(diào)用暴露出去

FilterProcessor

FilterProcessor是執(zhí)行過(guò)濾器的核心類

public class FilterProcessor {

    static FilterProcessor INSTANCE = new FilterProcessor();
    protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);

    private FilterUsageNotifier usageNotifier;


    public FilterProcessor() {
        usageNotifier = new BasicFilterUsageNotifier();
    }

    /**
     * @return the singleton FilterProcessor
     */
    public static FilterProcessor getInstance() {
        return INSTANCE;
    }

    /**
     * sets a singleton processor in case of a need to override default behavior
     *
     * @param processor
     */
    public static void setProcessor(FilterProcessor processor) {
        INSTANCE = processor;
    }

    /**
     * Override the default filter usage notification impl.
     *
     * @param notifier
     */
    public void setFilterUsageNotifier(FilterUsageNotifier notifier) {
        this.usageNotifier = notifier;
    }

    /**
     * 按順序執(zhí)行類型為post的過(guò)濾器列表(后置過(guò)濾器)
     * 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());
        }
    }

    /**
     * 按順序執(zhí)行類型為error的過(guò)濾器列表(錯(cuò)誤過(guò)濾器)
     * 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);
        }
    }

    /**
     * 按順序執(zhí)行類型為route的過(guò)濾器列表(路由過(guò)濾器)
     * 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());
        }
    }

    /**
     * 按順序執(zhí)行類型為pre的過(guò)濾器列表(前置過(guò)濾器)
     * 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());
        }
    }

    /**
     * 按順序執(zhí)行給定類型的過(guò)濾器列表
     * 通過(guò)FilterLoader加載所有給定類型type的過(guò)濾器
     * 循環(huán)遍歷過(guò)濾器列表依次執(zhí)行
     * 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();
            }
            
            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;
            }
        }
    }

    /**
     * Publishes a counter metric for each filter on each use.
     */
    public static class BasicFilterUsageNotifier implements FilterUsageNotifier {
        private static final String METRIC_PREFIX = "zuul.filter-";

        @Override
        public void notify(ZuulFilter filter, ExecutionStatus status) {
            DynamicCounter.increment(METRIC_PREFIX + filter.getClass().getSimpleName(), "status", status.name(), "filtertype", filter.filterType());
        }
    }
}

FilterProcessor 通過(guò)FilterLoader加載給定類型的過(guò)濾器列表,然后按順序依次執(zhí)行過(guò)濾器,根據(jù)過(guò)濾器的類型包含如下方法:

  • preRoute:按順序執(zhí)行類型為pre的過(guò)濾器列表(前置過(guò)濾器)
  • route:按順序執(zhí)行類型為route的過(guò)濾器列表(路由過(guò)濾器)
  • postRoute:按順序執(zhí)行類型為post的過(guò)濾器列表(后置過(guò)濾器)
  • error:按順序執(zhí)行類型為error的過(guò)濾器列表(錯(cuò)誤過(guò)濾器)

到此,zuul攔截servlet應(yīng)用http請(qǐng)求后通過(guò)過(guò)濾器列表進(jìn)行處理路由的整個(gè)流程以及代碼都查看完畢,對(duì)后期我們根據(jù)業(yè)務(wù)改造還是自定義過(guò)濾器有很大的指導(dǎo)意義。


備注 過(guò)濾器分類:

  • pre:前置過(guò)濾器
  • route:路由過(guò)濾器
  • post:后置過(guò)濾器
  • error:錯(cuò)誤過(guò)濾器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容