Tomcat源码解析——一次请求的处理流程

        在上一篇文章中,我们知道Tomcat在启动后,会在Connector中开启一个Acceptor(接收器)绑定线程然后用于监听socket的连接,那么当我们发出请求时,第一步也就是建立TCP连接,则会从Acceptor的run方法处进入。

Acceptor: 
       public void run() {
            int errorDelay = 0;
            //只有运行状态才进入while循环
            while (running) {
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    countUpOrAwaitConnection();

                    Socket socket = null;
                    try {
                        //阻塞接受TCP连接
                        socket = serverSocketFactory.acceptSocket(serverSocket);
                    } catch (IOException ioe) {
                        //...省略
                    }
                    errorDelay = 0;

                    //设置socket参数
                    if (running && !paused && setSocketOptions(socket)) {
                        //处理该socket(关键)
                        if (!processSocket(socket)) {
                            countDownConnection();
                            //处理不了则关闭socket
                            closeSocket(socket);
                        }
                    } else {
                        countDownConnection();
                        closeSocket(socket);
                    }
                } catch (IOException x) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), x);
                    }
                } catch (NullPointerException npe) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), npe);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }


    protected boolean processSocket(Socket socket) {
        try {
            SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
            wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
            wrapper.setSecure(isSSLEnabled());
            // During shutdown, executor may be null - avoid NPE
            if (!running) {
                return false;
            }
            //包装成一个socket执行器然后丢入线程池中处理(服务器是一对多的,所以不能阻塞TCP接收线程,必须异步处理)
            getExecutor().execute(new SocketProcessor(wrapper));
        } catch (RejectedExecutionException x) {
            log.warn("Socket processing request was rejected for:"+socket,x);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

        在Acceptor的run方法中进行while循环,然后阻塞接受一个TCP的连接,设置一些参数后包装成一个SocketProcessor丢入线程池中异步执行(因为服务器是一对多的,不能阻塞当前的接收TCP连接)。

SocketProcessor:
            public void run() {
            boolean launch = false;
            synchronized (socket) {
                try {
                    SocketState state = SocketState.OPEN;

                    try {
                        //SSL的预处理
                        // SSL handshake
                        serverSocketFactory.handshake(socket.getSocket());
                    } catch (Throwable t) {
                        //省略
                    }

                    if ((state != SocketState.CLOSED)) {
                        if (status == null) {
                            //交给对应的协议处理器去处理,因为Tomcat还支持AJP协议
                            state = handler.process(socket, SocketStatus.OPEN_READ);
                        } else {
                            state = handler.process(socket,status);
                        }
                    }
                    //处理过程中状态被设置为关闭则直接关闭socket
                    if (state == SocketState.CLOSED) {
                        countDownConnection();
                        try {
                            socket.getSocket().close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                    //...省略
                } finally {
                    if (launch) {
                         //如果不能处理,则兜底关闭连接
                         handler.process(socket, SocketStatus.DISCONNECT);
                    }
            }
        }


Http11ConnectionHandler:
    public SocketState process(SocketWrapper<S> wrapper,
                SocketStatus status) {
            //拿到对应的socket(因为Tomcat支持NIO、JIO、APR三种运行模式,所以需要用泛型)
            S socket = wrapper.getSocket();
            if (socket == null) {
                // Nothing to do. Socket has been closed.
                return SocketState.CLOSED;
            }
            //拿到最终的协议处理器
            Processor<S> processor = connections.get(socket);
            if (status == SocketStatus.DISCONNECT && processor == null) {
                return SocketState.CLOSED;
            }
            //...省略

            try {
                //真正的处理SSL
                initSsl(wrapper, processor);

                SocketState state = SocketState.CLOSED;
                do {
                    if (status == SocketStatus.CLOSE_NOW) {
                        processor.errorDispatch();
                        state = SocketState.CLOSED;
                    //...省略
                    } else {
                        //此处根据状态最终调用处理
                        state = processor.process(wrapper);
                    }
                }
                //...省略
        }
    }


Http11Processor:
    public SocketState process(SocketWrapper<S> socketWrapper)
        throws IOException {
        //获取IO
        setSocketWrapper(socketWrapper);
        getInputBuffer().init(socketWrapper, endpoint);
        getOutputBuffer().init(socketWrapper, endpoint);

        //基本请求参数设值,keepAlive默认是true
        keepAlive = true;
        comet = false;
        openSocket = false;
        sendfileInProgress = false;
        readComplete = true;
        try {
                setRequestLineReadTimeout();
                //解析请求行 HTTP协议三部分(请求首行、请求头、请求体,其中请求体一般是用户自己使用时解析)
                if (!getInputBuffer().parseRequestLine(keptAlive)) {
                    if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }

                 //读取请求头(只是把请求头解析成Map形式,并没有根据里面的值做一些处理,也就是初步解析)
                 if (!getInputBuffer().parseHeaders()) {
                        openSocket = true;
                        readComplete = false;
                        break;
                 }    

            if (!getErrorState().isError()) {
                try {
                    //真正的解析请求头,解析请求头每个Key里面的value,常用的则校验数据格式等
                    prepareRequest();
                } catch (Throwable t) {
                    //...省略
                }
            }
            //如果没有错误,则继续处理
            if (!getErrorState().isError()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    //调用适配器转交给最顶级容器(Engine)处理(关键)
                    adapter.service(request, response);
                    if(keepAlive && !getErrorState().isError() && (
                            response.getErrorException() != null ||
                                    (!isAsync() &&
                                    statusDropsConnection(response.getStatus())))) {
                        setErrorState(ErrorState.CLOSE_CLEAN, null);
                    }
                    setCometTimeouts(socketWrapper);
                } catch (InterruptedIOException e) {
                    setErrorState(ErrorState.CLOSE_NOW, e);
                } catch (HeadersTooLargeException e) {
                    if (response.isCommitted()) {
                        setErrorState(ErrorState.CLOSE_NOW, e);
                    } else {
                    }
                } catch (Throwable t) {
                    //...省略
                }
            }

            rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

            if (!isAsync() && !comet) {
                if (getErrorState().isError()) {
                    getInputBuffer().setSwallowInput(false);
                } else {
                    checkExpectationAndResponseStatus();
                }
                //结束一个请求
                endRequest();
            }

            //设置状态和一些响应信息
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
            if (getErrorState().isError()) {
                response.setStatus(500);
            }
            request.updateCounters();

            if (!isAsync() && !comet || getErrorState().isError()) {
                if (getErrorState().isIoAllowed()) {
                    getInputBuffer().nextRequest();
                    getOutputBuffer().nextRequest();
                }
            }

            if (!disableUploadTimeout) {
                if(endpoint.getSoTimeout() > 0) {
                    setSocketTimeout(endpoint.getSoTimeout());
                } else {
                    setSocketTimeout(0);
                }
            }

            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            if (breakKeepAliveLoop(socketWrapper)) {
                break;
            }
        }
        //...省略
    }

Adapter:
    public void service(org.apache.coyote.Request req,
                        org.apache.coyote.Response res)
        throws Exception {
//解析其它的HTTP参数,如?param=value(查询参数)、session和cookie处理、根据URL找到对应的Host和Contxt、Wrapper容器(因为要符合Servlet规范,很多参数都需要Tomcat为用户解析好)
boolean postParseSuccess = postParseRequest(req, request, res, response);
//真正转发给容器处理               
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

    }

        在SocketProcessor的run方法中,异步的进行解析,用对应的协议处理器处理,然后开始解析请求首行,请求头,等到解析完成并且没有错误时,则通过Adapter(适配器)的service方法,将请求传递给容器(Engine、Host、Context、Wrapper)。

        在组件介绍的文章中,我们知道Engine、Host、Context、Wrapper都属于容器Container,有共同的特性,如一样的生命周期、都有Pipeline(管道)、管道中都存在Valve(阀)。

        在Adapter的service方法中,获取的是Engine中Pipeline的第一个Valve去处理。

        Pipeline中有两种Valve,一种是普通Valve,一种是基础Valve。普通Valve也就是用户自定义添加使用的,而基础Vavle是用于当前容器兜底和传递给下一个容器使用的,所以在Adapter中传递下去的Request和Response最终要从上至下经过一个个的容器中的Vavle处理。

StandardEngineValve:
        public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        //从请求中获取对应的Host
        Host host = request.getHost();
        //调用Host的Valve
        host.getPipeline().getFirst().invoke(request, response);

    }



StandardHostValve:
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        Context context = request.getContext();

        if( context.getLoader() != null ) {
                //此处为当前线程设置应用的类加载器,从而实现应用之间class隔离(重点)
             Thread.currentThread().setContextClassLoader
                        (context.getLoader().getClassLoader());
        }
        //...省略
        //调用Context的Valve
        context.getPipeline().getFirst().invoke(request, response);
    }


StandardContextValve:

        public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        //确认请求
        response.sendAcknowledgement();
        //...省略
        //传递到最后一个Wrapper容器中处理
        wrapper.getPipeline().getFirst().invoke(request, response);
    }


StandardWrapperValve:
        public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        boolean unavailable = false;
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        if (!unavailable) {
            //通过Wrapper容器获取或创建一个Servlet
           servlet = wrapper.allocate();
        }
        //...省略
        //根据请求信息创建一条过滤器链
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);

        try {
             //调用过滤器链处理请求,这就是我们经常配置的Filter过滤器
             filterChain.doFilter(request.getRequest(), response.getResponse());
        } catch (Throwable e) {
            //异常处理
            exception(request, response, e);
        }

    }

StandardWrapper:
        public Servlet allocate() throws ServletException {

        if (!singleThreadModel) {
            //此处可以看到使用的是双重校验加锁的单例模式,所以Wrapper容器其实就是Servlet的包装,一个Wrapper容器对应一个单例的Servlet
            if (instance == null) {
                synchronized (this) {
                    if (instance == null) {
                        //通过反射创建自定义的Servlet对象
                        instance = loadServlet();
                        if (!singleThreadModel) {
                            newInstance = true;
                            countAllocated.incrementAndGet();
                        }
                    }
                }
            }
            //如果没有初始化则初始化
            if (!instanceInitialized) {
                initServlet(instance);
            }

    }

    private synchronized void initServlet(Servlet servlet)
            throws ServletException {
        //初始化调用Servlet的init方法
        servlet.init(facade);
    }


ApplicationFilterChain:
    public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

        internalDoFilter(request,response);
    }

    private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {
            ApplicationFilterConfig filterConfig = filters[pos++];
            Filter filter = null;
         try {
            filter = filterConfig.getFilter();
            //调用过滤器执行
            filter.doFilter(request, response, this);
        } catch (Throwable e) {
                //省略...异常处理
        }
        if ((request instanceof HttpServletRequest) &&
            (response instanceof HttpServletResponse)) {
                //最终调用到用户自定义的Servlet的service方法中
                servlet.service(request, response);
        }

    }    

         Engine把Request通过PipeLine中的Valve传递到最终的Wrapper中,用户可以在这几个容器(Engine、Host、Context、Wrapper)的PipeLine添加普通的Valve进行一些操作。

        终于,在Wrapper中,Tomcat通过单例模式创建出用户自定义的Servlet,然后经过过滤器链的处理后,调用到Servlet的service方法中,此方法也是给用户实现处理请求的最终方法。

        时序图:

                

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/566978.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用户体验至上:独立站脱颖而出的关键要素解析

在数字化时代&#xff0c;独立站成为了许多品牌和企业展示自身形象、推广产品、建立客户联系的重要平台。然而&#xff0c;要想在众多的独立站中脱颖而出&#xff0c;吸引并留住用户&#xff0c;良好的用户体验至关重要。本文Nox聚星将和大家探讨如何做好独立站的用户体验&…

antDesignPro ProForm表单里使用dependencies属性

场景&#xff1a;ProForm表单里前一个下拉框选择的值带出后面下拉框的枚举值 <script><ProFormformRef{formRef}onFinish{{}}><ProForm.Group><ProFormSelectname"projectId"label"项目"width"sm"request{projectList}plac…

echerts饼图分割操作

在饼图制作中遇到了一个难点就是饼图中间是分散的 试了很多方法&#xff0c;最后选择了给每个值中间再加一节的处理方式&#xff0c;并把颜色设置为透明就能达到相同效果。 处理后的样式&#xff1a; 代码&#xff1a; let list this.data.list;/饼图内部展示数据// let _t…

文心一言 VS 讯飞星火 VS chatgpt (242)-- 算法导论17.4 1题

一、假定我们希望实现一个动态的开地址散列表。为什么我们需要当装载因子达到一个严格小于 1 的值 a 时就认为表满&#xff1f;简要描述如何为动态开地址散列表设计一个插入算法&#xff0c;使得每个插入操作的摊还代价的期望值为 O(1) 。为什么每个插入操作的实际代价的期望值…

React基础知识大汇总

函数组件和类组件 函数组件与类组件有什么区别呢&#xff1f; function getName(params:{name:string}){const count 0;return params.name -count; } getName({name:"test"}) getName({name:"哈哈哈"})getName是一个纯函数&#xff0c;不产生任何副作用…

54、图论-实现Trie前缀树

思路&#xff1a; 主要是构建一个trie前缀树结构。如果构建呢&#xff1f;看题意&#xff0c;应该当前节点对象下有几个属性&#xff1a; 1、next节点数组 2、是否为结尾 3、当前值 代码如下&#xff1a; class Trie {class Node {boolean end;Node[] nexts;public Node(…

nginx配置挂载html

目标 很多软件的官方文档&#xff0c;在国内打开很慢&#xff0c;每次都得等很久&#xff0c;看到官方同时提供了html的包&#xff0c;所以想着挂载到本地nginx下&#xff0c;查看会方便很多。 下载官方html文档包&#xff0c;解压到documentation_htmls下 想添加新的文档也是…

Sql Server 数据库:查询表结构脚本

查询脚本: SELECT CASE WHEN col.colorder 1 THEN obj.name ELSE END AS 表名, col.colorder AS 序号 , col.name AS 列名 , ISNULL(ep.[value], ) AS 列说明 , t.name AS 数据类型 , col.length AS 长度 , ISNULL(COLUMNPROPERTY(col.id, col.name, Scale), 0) AS 小数位数…

<开源> 轮廓内缩外扩算法

轮廓内缩外扩算法 项目是论文A new offset algorithm for closed 2D lines with Islands的JAVA实现。 项目的GitHub地址&#xff1a;https://github.com/Lee-0o0/polygon-offset-algorithm。 参考博客 https://blog.csdn.net/qq_41261251/article/details/114462696

设计模式 -- 行为型模式

1. 行为型模式概述 行为型模式用于描述程序在运行时复杂的流程控制&#xff0c;即描述多个类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务&#xff0c;它涉及算法与对象间职责的分配。 行为型模式分为类行为模式和对象行为模式&#xff0c;前者采用继承机制在类…

java开发之路——node.js安装

1. 安装node.js 最新Node.js安装详细教程及node.js配置 (1)默认的全局的安装路径和缓存路径 npm安装模块或库(可以统称为包)常用的两种命令形式&#xff1a; 本地安装(local)&#xff1a;npm install 名称全局安装(global)&#xff1a;npm install 名称 -g本地安装和全局安装…

input的type=‘radio‘设置只读属性颜色为灰色,如何修改

目录 1.设置input和label的样式为不可点击。 2.设置input的readonly属性。 3.若想变回可修改&#xff0c;用js实现 4.如何自定义radio的颜色。 5.完整代码 input的单选框有时候需要实现只读&#xff0c;两个办法&#xff0c;一个disabled&#xff0c;一个是readonly. 但d…

前期Hadoop学习总结

前期Hadoop学习总结 1.Linux&#xff1a;操作系统 ​ 2.虚拟机&#xff1a;主机 3.SecureCRT &#xff08;客户端&#xff09;&#xff1a;连接Linux 方便操作 4.Hadoop&#xff1a;软件 这个软件要装在Linux里面 5.Hadoop是干嘛的&#xff1a; Hadoop是一个开源的分布式计…

前端路由的实现原理

当谈到前端路由时&#xff0c;指的是在前端应用中管理页面导航和URL的机制。前端路由使得单页应用&#xff08;Single-Page Application&#xff0c;SPA&#xff09;能够在用户与应用交互时动态地加载不同的视图&#xff0c;而无需每次都重新加载整个页面。 在前端开发中&…

货拉拉0-1数据指标体系构建与应用

目录 一、背景 二、指标体系搭建 2.1 指标设计 2.2 指标体系搭建 2.3 指标维度拆解 三、指标标准化建设 四、指标元数据管理 五、指标应用&未来规划 原文大佬介绍的这篇指标体系构建有借鉴意义&#xff0c;现摘抄下来用作沉淀学习。如有侵权请告知~ 一、背景 指标…

什么是仪器校准报告?

在科学实验和工业生产中&#xff0c;仪器是一种非常重要的辅助工具&#xff0c;无论是测量数据、控制实验进程还是保证产品质量&#xff0c;仪器都发挥着至关重要的作用。为了确保仪器的准确性和稳定性&#xff0c;仪器校准报告这一概念应运而生。本文给大家详细介绍仪器校准报…

利用STM32的定时器和中断实现精准时间控制

⬇帮大家整理了单片机的资料 包括stm32的项目合集【源码开发文档】 点击下方蓝字即可领取&#xff0c;感谢支持&#xff01;⬇ 点击领取更多嵌入式详细资料 问题讨论&#xff0c;stm32的资料领取可以私信&#xff01; 在嵌入式系统开发中&#xff0c;精确的时间控制是许多应用的…

0元实现网站HTTP升级到HTTPS(免费https证书)

HTTPS就是在HTTP的基础上加入了SSL&#xff0c;将一个使用HTTP的网站免费升级到HTTPS主要包括以下几个步骤&#xff1a; 1 获取SSL证书 永久免费的https证书申请通道https://www.joyssl.com/certificate/select/free.html?nid16 免费的SSL证书同样能实现HTTPS&#xff0c;国…

【前端】vue的基础知识及开发指引

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Vue是什么二、学习 Vue.js 的基础知识三、熟悉 Vue.js 的生态系统四、掌握常用工具和库五、实践和项目开发六、 持续学习和跟进 前言 随着开发语言及人工智…

[Windows] Bypass分流抢票 v1.16.25 五一黄金周自动抢票软件(2024.02.08更新)

五一黄金周要来了&#xff0c;火车票难买到&#xff0c;即便官网候选订票也要看运气&#xff0c;推荐使用这个靠谱的自动抢票软件&#xff0c; 该工具是目前市面上最好用口碑最好的电脑抢票软件&#xff0c;从13年到现在&#xff0c;作者依旧在更新&#xff0c;可以自动识别123…
最新文章