Tomcat源码分析 (九)----- HTTP请求处理过程(二)

本文接着上一篇文章的容器处理继续讲解,详细介绍了Tomcat中Engine、Host、Context和Wrapper处理请求的流程。如Engine选择合适的Host,Host处理时检测异常等。还罗列了Wrapper处理请求的几个关键点,包括分配Servlet实例、创建过滤器链等,并对部分关键点进行了源码分析。

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

我们接着上一篇文章的容器处理来讲,当postParseRequest方法返回true时,则由容器继续处理,在service方法中有 connector.getService().getContainer().getPipeline().getFirst().invoke(request, response) 这一行:

  • Connector调用getService()返回StandardService;
  • StandardService调用getContainer返回StandardEngine;
  • StandardEngine调用getPipeline返回与其关联的StandardPipeline;

Engine处理请求

我们在前面的文章中讲过 StandardEngine 的构造函数为自己的Pipeline添加了基本阀 StandardEngineValve ,代码如下:

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

接下来我们看看StandardEngineValveinvoke()方法。该方法主要是选择合适的Host,然后调用Host中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); 
    }

该方法很简单,校验该Engline 容器是否含有Host容器,如果不存在,返回400错误,否则继续执行 host.getPipeline().getFirst().invoke(request, response),可以看到 Host 容器先获取自己的管道,再获取第一个阀门,我们再看看该阀门的 invoke 方法。

Host处理请求

分析Host的时候,我们从Host的构造函数入手,该方法主要是设置基础阀门。

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

StandardPipeline调用getFirst得到第一个阀去处理请求,由于基本阀是最后一个,所以最后会由基本阀去处理请求。

StandardHost的Pipeline里面一定有 ErrorReportValve 与 StandardHostValve两个Valve,ErrorReportValve主要是检测 Http 请求过程中是否出现过什么异常, 有异常的话, 直接拼装 html 页面, 输出到客户端。

我们看看ErrorReportValve的invoke方法:

    public void invoke(Request request, Response response)
        throws IOException, ServletException {
        // Perform the request
        // 1. 先将 请求转发给下一个 Valve
        getNext().invoke(request, response);  
        // 2. 这里的 isCommitted 表明, 请求是正常处理结束    
        if (response.isCommitted()) {               
            return;
        }
        // 3. 判断请求过程中是否有异常发生
        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 里面的数据(此时 Response 里面可能有些数据)
                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. 若是异步请求的话, 设置对应的 complete (对应的是 异步 Servlet)                   
            request.getAsyncContext().complete();
        }
    }

该方法首先执行了下个阀门的 invoke 方法。然后根据返回的Request 属性设置一些错误信息。那么下个阀门是谁呢?其实就是基础阀门了:StandardHostValve,该阀门的 invoke 的方法是如何实现的呢?

    @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
        boolea
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值