自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

【Tomcat源碼分析】從零開始理解 HTTP 請求處理

開發(fā) 前端
承接上文容器處理機(jī)制,當(dāng) postParseRequest方法返回真值時,容器將繼續(xù)處理請求。在 service 方法中,通過 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response) 這行代碼,容器將請求傳遞至管道的第一步,開啟后續(xù)的處理流程。

前言

承接上文容器處理機(jī)制,當(dāng) postParseRequest方法返回真值時,容器將繼續(xù)處理請求。在 service 方法中,通過 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response) 這行代碼,容器將請求傳遞至管道的第一步,開啟后續(xù)的處理流程。

  • Connector 調(diào)用 getService() 方法返回 StandardService 對象,
  • StandardService 接著調(diào)用 getContainer() 方法返回 StandardEngine 對象,
  • 最終 StandardEngine 調(diào)用 getPipeline() 方法返回與其關(guān)聯(lián)的 StandardPipeline 對象。

Engine 處理請求

回顧前文,StandardEngine 的構(gòu)造函數(shù)中,為其關(guān)聯(lián)的 Pipeline 添加了名為 StandardEngineValve 的基本閥,代碼如下:

public StandardEngine() {
    super();
    pipeline.setBasic(new StandardEngineValve());
    try {
        setJvmRoute(System.getProperty("jvmRoute"));
    } catch(Exception ex) {
        log.warn(sm.getString("standardEngine.jvmRouteFail"));
    }
}

接下來,我們深入探究 StandardEngineValve 的 invoke() 方法。該方法主要負(fù)責(zé)選擇合適的 Host,并調(diào)用該 Host 所關(guān)聯(lián)的 Pipeline 中第一個 Valve 的 invoke() 方法,將請求傳遞至下一處理階段。

public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Host to be used for this Request
    Host host = request.getHost();
    if (host == null) {
        response.sendError
            (HttpServletResponse.SC_BAD_REQUEST,
             sm.getString("standardEngine.noHost",
                          request.getServerName()));
        return;
    }
    if (request.isAsyncSupported()) {
        request.setAsyncSupported(host.getPipeline().isAsyncSupported());
    }

    // Ask this Host to process this request
    host.getPipeline().getFirst().invoke(request, response);
}

StandardEngineValve 的 invoke() 方法邏輯清晰,首先檢查當(dāng)前 Engine 容器是否包含 Host 容器,若不存在,則返回 400 錯誤。若存在,則繼續(xù)執(zhí)行 host.getPipeline().getFirst().invoke(request, response)??梢钥吹剑琀ost 容器先獲取其關(guān)聯(lián)的 Pipeline,再獲取 Pipeline 中的第一個 Valve,并調(diào)用該 Valve 的 invoke() 方法,將請求傳遞至下一個處理階段。

Host 處理請求

分析 Host 時,我們從其構(gòu)造函數(shù)入手,該方法主要負(fù)責(zé)設(shè)置基礎(chǔ)閥門。

public StandardHost() {
    super();
    pipeline.setBasic(new StandardHostValve());
}

StandardPipeline 調(diào)用 getFirst 方法獲取第一個 Valve 來處理請求。由于基本閥通常是最后一個添加的 Valve,因此最終請求會由基本閥進(jìn)行處理。

StandardHost 的 Pipeline 中必然包含 ErrorReportValve 和 StandardHostValve 兩個 Valve。ErrorReportValve 主要負(fù)責(zé)檢測 HTTP 請求過程中是否出現(xiàn)過異常,如果有異常,則直接拼裝 HTML 頁面,并輸出到客戶端。

接下來,我們仔細(xì)觀察 ErrorReportValve 的 invoke() 方法:

public void invoke(Request request, Response response)
    throws IOException, ServletException {
    // Perform the request
    // 1. 先將 請求轉(zhuǎn)發(fā)給下一個 Valve
    getNext().invoke(request, response);
    // 2. 這里的 isCommitted 表明, 請求是正常處理結(jié)束
    if (response.isCommitted()) {
        return;
    }
    // 3. 判斷請求過程中是否有異常發(fā)生
    Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
    if (request.isAsyncStarted() && ((response.getStatus() < 400 &&
            throwable == null) || request.isAsyncDispatching())) {
        return;
    }
    if (throwable != null) {
        // The response is an error
        response.setError();
        // Reset the response (if possible)
        try {
            // 4. 重置 response 里面的數(shù)據(jù)(此時 Response 里面可能有些數(shù)據(jù))
            response.reset();
        } catch (IllegalStateException e) {
            // Ignore
        }
         // 5. 這就是我們??吹降?500 錯誤碼
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
    }
    response.setSuspended(false);
    try {
        // 6. 這里就是將 異常的堆棧信息組合成 html 頁面, 輸出到前臺
        report(request, response, throwable);
    } catch (Throwable tt) {
        ExceptionUtils.handleThrowable(tt);
    }
    if (request.isAsyncStarted()) {
        // 7. 若是異步請求的話, 設(shè)置對應(yīng)的 complete (對應(yīng)的是 異步 Servlet)
        request.getAsyncContext().complete();
    }
}

ErrorReportValve 的 invoke() 方法首先執(zhí)行下一個 Valve 的 invoke() 方法,然后根據(jù)返回的 Request 屬性設(shè)置一些錯誤信息。那么下一個 Valve 是誰呢?實(shí)際上就是基本閥門 StandardHostValve。

接下來,我們深入分析 StandardHostValve 的 invoke() 方法是如何實(shí)現(xiàn)的:

@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Context to be used for this Request
    Context context = request.getContext();
    if (context == null) {
        response.sendError
            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
             sm.getString("standardHost.noContext"));
        return;
    }

    // Bind the context CL to the current thread
    if( context.getLoader() != null ) {
        // Not started - it should check for availability first
        // This should eventually move to Engine, it's generic.
        if (Globals.IS_SECURITY_ENABLED) {
            PrivilegedAction<Void> pa = new PrivilegedSetTccl(
                    context.getLoader().getClassLoader());
            AccessController.doPrivileged(pa);
        } else {
            Thread.currentThread().setContextClassLoader
                    (context.getLoader().getClassLoader());
        }
    }
    if (request.isAsyncSupported()) {
        request.setAsyncSupported(context.getPipeline().isAsyncSupported());
    }

    // Don't fire listeners during async processing
    // If a request init listener throws an exception, the request is
    // aborted
    boolean asyncAtStart = request.isAsync();
    // An async error page may dispatch to another resource. This flag helps
    // ensure an infinite error handling loop is not entered
    boolean errorAtStart = response.isError();
    if (asyncAtStart || context.fireRequestInitEvent(request)) {

        // Ask this Context to process this request
        try {
            context.getPipeline().getFirst().invoke(request, response);
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            if (errorAtStart) {
                container.getLogger().error("Exception Processing " +
                        request.getRequestURI(), t);
            } else {
                request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
                throwable(request, response, t);
            }
        }

        // If the request was async at the start and an error occurred then
        // the async error handling will kick-in and that will fire the
        // request destroyed event *after* the error handling has taken
        // place
        if (!(request.isAsync() || (asyncAtStart &&
                request.getAttribute(
                        RequestDispatcher.ERROR_EXCEPTION) != null))) {
            // Protect against NPEs if context was destroyed during a
            // long running request.
            if (context.getState().isAvailable()) {
                if (!errorAtStart) {
                    // Error page processing
                    response.setSuspended(false);

                    Throwable t = (Throwable) request.getAttribute(
                            RequestDispatcher.ERROR_EXCEPTION);

                    if (t != null) {
                        throwable(request, response, t);
                    } else {
                        status(request, response);
                    }
                }

                context.fireRequestDestroyEvent(request);
            }
        }
    }

    // Access a session (if present) to update last accessed time, based on a
    // strict interpretation of the specification
    if (ACCESS_SESSION) {
        request.getSession(false);
    }

    // Restore the context classloader
    if (Globals.IS_SECURITY_ENABLED) {
        PrivilegedAction<Void> pa = new PrivilegedSetTccl(
                StandardHostValve.class.getClassLoader());
        AccessController.doPrivileged(pa);
    } else {
        Thread.currentThread().setContextClassLoader
                (StandardHostValve.class.getClassLoader());
    }
}

StandardHostValve 的 invoke() 方法首先校驗(yàn) Request 是否存在 Context,這個 Context 在執(zhí)行 CoyoteAdapter.postParseRequest 方法時就已經(jīng)設(shè)置好了。如果 Context 不存在,則返回 500 錯誤。接下來,依然是熟悉的流程:context.getPipeline().getFirst().invoke,該管道獲取的是基礎(chǔ)閥門 StandardContextValve,我們繼續(xù)關(guān)注它的 invoke() 方法。

Context 處理請求

接著,Context 會去處理請求,同樣地,StandardContextValve 的 invoke() 方法會被調(diào)用:

@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {
    // Disallow any direct access to resources under WEB-INF or META-INF
    MessageBytes requestPathMB = request.getRequestPathMB();
    if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/META-INF"))
            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Select the Wrapper to be used for this Request
    Wrapper wrapper = request.getWrapper();
    if (wrapper == null || wrapper.isUnavailable()) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Acknowledge the request
    try {
        response.sendAcknowledgement();
    } catch (IOException ioe) {
        container.getLogger().error(sm.getString(
                "standardContextValve.acknowledgeException"), ioe);
        request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        return;
    }

    if (request.isAsyncSupported()) {
        request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());
    }
    wrapper.getPipeline().getFirst().invoke(request, response);
}

Wrapper 處理請求

Wrapper 是一個 Servlet 的包裝,我們先來了解一下它的構(gòu)造方法。其主要作用是設(shè)置基礎(chǔ)閥門 StandardWrapperValve。

接下來,我們來分析 StandardWrapperValve 的 invoke() 方法。

@Override
public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Initialize local variables we may need
    boolean unavailable = false;
    Throwable throwable = null;
    // This should be a Request attribute...
    long t1=System.currentTimeMillis();
    requestCount.incrementAndGet();
    StandardWrapper wrapper = (StandardWrapper) getContainer();
    Servlet servlet = null;
    Context context = (Context) wrapper.getParent();

    // Check for the application being marked unavailable
    if (!context.getState().isAvailable()) {
        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                       sm.getString("standardContext.isUnavailable"));
        unavailable = true;
    }

    // Check for the servlet being marked unavailable
    if (!unavailable && wrapper.isUnavailable()) {
        container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
                wrapper.getName()));
        long available = wrapper.getAvailable();
        if ((available > 0L) && (available < Long.MAX_VALUE)) {
            response.setDateHeader("Retry-After", available);
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                    sm.getString("standardWrapper.isUnavailable",
                            wrapper.getName()));
        } else if (available == Long.MAX_VALUE) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                    sm.getString("standardWrapper.notFound",
                            wrapper.getName()));
        }
        unavailable = true;
    }

    // Allocate a servlet instance to process this request
    try {
        // 關(guān)鍵點(diǎn)1:這兒調(diào)用Wrapper的allocate()方法分配一個Servlet實(shí)例
        if (!unavailable) {
            servlet = wrapper.allocate();
        }
    } catch (UnavailableException e) {
        container.getLogger().error(
                sm.getString("standardWrapper.allocateException",
                        wrapper.getName()), e);
        long available = wrapper.getAvailable();
        if ((available > 0L) && (available < Long.MAX_VALUE)) {
            response.setDateHeader("Retry-After", available);
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                       sm.getString("standardWrapper.isUnavailable",
                                    wrapper.getName()));
        } else if (available == Long.MAX_VALUE) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                       sm.getString("standardWrapper.notFound",
                                    wrapper.getName()));
        }
    } catch (ServletException e) {
        container.getLogger().error(sm.getString("standardWrapper.allocateException",
                         wrapper.getName()), StandardWrapper.getRootCause(e));
        throwable = e;
        exception(request, response, e);
    } catch (Throwable e) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString("standardWrapper.allocateException",
                         wrapper.getName()), e);
        throwable = e;
        exception(request, response, e);
        servlet = null;
    }

    MessageBytes requestPathMB = request.getRequestPathMB();
    DispatcherType dispatcherType = DispatcherType.REQUEST;
    if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
    request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);
    request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
            requestPathMB);
    // Create the filter chain for this request
    // 關(guān)鍵點(diǎn)2,創(chuàng)建過濾器鏈,類似于Pipeline的功能
    ApplicationFilterChain filterChain =
            ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);

    // Call the filter chain for this request
    // NOTE: This also calls the servlet's service() method
    try {
        if ((servlet != null) && (filterChain != null)) {
            // Swallow output if needed
            if (context.getSwallowOutput()) {
                try {
                    SystemLogHandler.startCapture();
                    if (request.isAsyncDispatching()) {
                        request.getAsyncContextInternal().doInternalDispatch();
                    } else {
                        // 關(guān)鍵點(diǎn)3,調(diào)用過濾器鏈的doFilter,最終會調(diào)用到Servlet的service方法
                        filterChain.doFilter(request.getRequest(),
                                response.getResponse());
                    }
                } finally {
                    String log = SystemLogHandler.stopCapture();
                    if (log != null && log.length() > 0) {
                        context.getLogger().info(log);
                    }
                }
            } else {
                if (request.isAsyncDispatching()) {
                    request.getAsyncContextInternal().doInternalDispatch();
                } else {
                    // 關(guān)鍵點(diǎn)3,調(diào)用過濾器鏈的doFilter,最終會調(diào)用到Servlet的service方法
                    filterChain.doFilter
                        (request.getRequest(), response.getResponse());
                }
            }

        }
    } catch (ClientAbortException e) {
        throwable = e;
        exception(request, response, e);
    } catch (IOException e) {
        container.getLogger().error(sm.getString(
                "standardWrapper.serviceException", wrapper.getName(),
                context.getName()), e);
        throwable = e;
        exception(request, response, e);
    } catch (UnavailableException e) {
        container.getLogger().error(sm.getString(
                "standardWrapper.serviceException", wrapper.getName(),
                context.getName()), e);
        //            throwable = e;
        //            exception(request, response, e);
        wrapper.unavailable(e);
        long available = wrapper.getAvailable();
        if ((available > 0L) && (available < Long.MAX_VALUE)) {
            response.setDateHeader("Retry-After", available);
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                       sm.getString("standardWrapper.isUnavailable",
                                    wrapper.getName()));
        } else if (available == Long.MAX_VALUE) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND,
                        sm.getString("standardWrapper.notFound",
                                    wrapper.getName()));
        }
        // Do not save exception in 'throwable', because we
        // do not want to do exception(request, response, e) processing
    } catch (ServletException e) {
        Throwable rootCause = StandardWrapper.getRootCause(e);
        if (!(rootCause instanceof ClientAbortException)) {
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceExceptionRoot",
                    wrapper.getName(), context.getName(), e.getMessage()),
                    rootCause);
        }
        throwable = e;
        exception(request, response, e);
    } catch (Throwable e) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString(
                "standardWrapper.serviceException", wrapper.getName(),
                context.getName()), e);
        throwable = e;
        exception(request, response, e);
    }

    // Release the filter chain (if any) for this request
    // 關(guān)鍵點(diǎn)4,釋放掉過濾器鏈及其相關(guān)資源
    if (filterChain != null) {
        filterChain.release();
    }

    // 關(guān)鍵點(diǎn)5,釋放掉Servlet及相關(guān)資源
    // Deallocate the allocated servlet instance
    try {
        if (servlet != null) {
            wrapper.deallocate(servlet);
        }
    } catch (Throwable e) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString("standardWrapper.deallocateException",
                         wrapper.getName()), e);
        if (throwable == null) {
            throwable = e;
            exception(request, response, e);
        }
    }

    // If this servlet has been marked permanently unavailable,
    // unload it and release this instance
    // 關(guān)鍵點(diǎn)6,如果servlet被標(biāo)記為永遠(yuǎn)不可達(dá),則需要卸載掉它,并釋放這個servlet實(shí)例
    try {
        if ((servlet != null) &&
            (wrapper.getAvailable() == Long.MAX_VALUE)) {
            wrapper.unload();
        }
    } catch (Throwable e) {
        ExceptionUtils.handleThrowable(e);
        container.getLogger().error(sm.getString("standardWrapper.unloadException",
                         wrapper.getName()), e);
        if (throwable == null) {
            throwable = e;
            exception(request, response, e);
        }
    }
    long t2=System.currentTimeMillis();

    long time=t2-t1;
    processingTime += time;
    if( time > maxTime) maxTime=time;
    if( time < minTime) minTime=time;
}


責(zé)任編輯:武曉燕 來源: 碼上遇見你
相關(guān)推薦

2024-09-18 08:10:06

2019-01-18 12:39:45

云計(jì)算PaaS公有云

2018-09-14 17:16:22

云計(jì)算軟件計(jì)算機(jī)網(wǎng)絡(luò)

2024-11-27 16:25:54

JVMJIT編譯機(jī)制

2024-11-18 17:31:27

2020-07-02 15:32:23

Kubernetes容器架構(gòu)

2024-12-06 17:02:26

2015-11-17 16:11:07

Code Review

2018-04-18 07:01:59

Docker容器虛擬機(jī)

2021-10-29 08:07:30

Java timeout Java 基礎(chǔ)

2023-11-14 16:14:49

2024-05-15 14:29:45

2023-11-09 23:45:01

Pytorch目標(biāo)檢測

2010-05-26 17:35:08

配置Xcode SVN

2024-11-28 10:35:47

2024-04-10 07:48:41

搜索引擎場景

2015-10-15 14:16:24

2011-04-06 15:55:50

開發(fā)webOS程序webOS

2017-02-10 09:30:33

數(shù)據(jù)化運(yùn)營流量

2010-02-22 09:39:52

HTML 5Web
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號