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

news2024/11/18 17:27:34

        在上一篇文章中,我们知道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.coloradmin.cn/o/1621081.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

29 OpenCV 图像距

文章目录 距的概念API函数示例 距的概念 距的概念 API函数 moments( InputArray array,//输入数据 bool binaryImagefalse // 是否为二值图像 )contourArea( InputArray contour,//输入轮廓数据 bool oriented// 默认false、返回绝对值)arcLength( InputArray curve…

springboot结合elasticJob

先说一说什么是elasticJob。 ElasticJob是一个分布式任务调度的解决方案&#xff0c;它由俩个相互独立的子项目Elastic-job-lite和Elastic- job-cloud组成。 任务调度&#xff1a;是指系统为了自动完成特定任务&#xff0c;在任务的特定时刻去执行任务的过程。 分布式&#xf…

BUUCTF---misc---[SWPU2019]我有一只马里奥

1、下载附件是一个.exe文件 2、运行之后可以看到桌面生成了1.txt文件&#xff0c;文件里面有如下内容 3、经过信息搜索&#xff1a;NTFS&#xff08;New Technology File System&#xff09;是一种由Microsoft开发的专有日志文件系统。根据它的提示&#xff0c;应该是把flag.tx…

安装Fake UserAgent 库的方法最终解答!_Python库

安装Python库Fake UserAgent 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 Fake UserAgent Fake UserAgent 是一个Python库&#xff0c;用于生成随机或特定的用户代理&#xff08;UserAgent&#xff09;字符串。用户…

C++:模板(初级)

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;模板&#xff08;初级&#xff09;》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞…

零基础转行网络安全,难度大吗?

说有难度那是肯定会有的&#xff0c;事在人为&#xff0c;我之前是从事于Java后端开发的&#xff0c;后面转行学网络安全&#xff0c;花了些时间&#xff0c;现在拿到了比之前开发更高的薪资&#xff0c;觉得还是挺满足的&#xff01; 1.网络安全岗位 1.1安全运维工程师 负责监…

Ubuntu Pycharm安装

下载PyCharm&#xff0c;https://www.jetbrains.com/pycharm/download/?sectionlinux 然后按照下图执行安装&#xff1a; 安装的时候可能出现的问题&#xff1a; 问题1&#xff1a;No JDK found. Please validate either PYCHARM_JDK, JDK_HOME or JAVA_HOME environment var…

代码随想录第44天|动态规划:完全背包理论基础 518.零钱兑换II 377. 组合总和 Ⅳ

动态规划&#xff1a;完全背包理论基础 代码随想录 (programmercarl.com) 动态规划之完全背包&#xff0c;装满背包有多少种方法&#xff1f;组合与排列有讲究&#xff01;| LeetCode&#xff1a;518.零钱兑换II_哔哩哔哩_bilibili 完全背包和01背包问题唯一不同的地方就是&…

5.组合与继承

1.面向对象 在C中&#xff0c;面向对象&#xff08;Object-Oriented&#xff09;是一种程序设计范式&#xff0c;它使用“对象”来设计应用程序和软件。面向对象编程&#xff08;OOP&#xff09;的核心概念包括类&#xff08;Class&#xff09;、对象&#xff08;Object&#x…

设计模式——状态模式19

状态模式是一种行为设计模式&#xff0c; 允许一个对象在其内部状态改变时改变它的行为&#xff0c;对象看起来好像修改了它的类。状态模式的核心是状态与行为绑定&#xff0c;不同的状态对应不同的行为。 设计模式&#xff0c;一定要敲代码理解 状态行为抽象 //在某种状态下&…

多模态模型

转换器成功作为构建语言模型的一种方法&#xff0c;促使 AI 研究人员考虑同样的方法是否对图像数据也有效。 研究结果是开发多模态模型&#xff0c;其中模型使用大量带有描述文字的图像进行训练&#xff0c;没有固定的标签。 图像编码器基于像素值从图像中提取特征&#xff0c;…

《十》Qt各种对话框之QFontDialog

QFontDialog 在介绍 QFontDialog 对话框之前&#xff0c;我们先简单介绍一下 QFont 字体类。QFont 主要用于控制文本显示的字体&#xff0c;字体主要有四大属性&#xff1a;①字体家族 family 决定字体外观家族&#xff0c;比如宋体、楷体等&#xff1b; ②字号 pointSize &am…

用于车载T-BOX汽车级的RA8900CE

用于车载T-BOX等高精度计时的汽车级时钟模块RTC:RA8900CE.车载实时时钟芯片RA8900CE内置32.768Khz的晶体&#xff0c;实现年、月、日、星期、小时、分钟和秒精准计时。RA8900CE满足AEC-Q200认证&#xff0c;内置温补功能&#xff0c;保证实时时钟的稳定可靠&#xff0c;功耗低至…

huggingface模型下载至本地并调用教程

huggingface内有许多预训练模型&#xff0c;可以在线调用模型或者将模型部署至本地&#xff0c;但有时候通过网址调用模型会很慢&#xff0c;有些服务器甚至无法通过网址调用… 那么&#xff0c;正题&#xff0c;如何将huggingface的模型部署至本地呢&#xff1f;其实很简单&am…

MongoDB磁盘空间占满,导致数据库被锁定,如何清理数据和磁盘空间

一、问题 1、我在实际项目中&#xff0c;遇到一个问题&#xff0c;随着数据每天的不断增加&#xff0c;导致mongodb的磁盘空间站满了&#xff0c;数据库被锁了&#xff0c;无法使用。 2、故障表现 部署的应用程序突然无法将数据写入数据库&#xff0c;但是可以正常读取数据。…

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件&#xff0c;它能够让普通的家用路由器具备高级路由功能&#xff0c;提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP&#xff08;SSH文件传输协议&#xff09;服务&#xff0c;以便…

SpringBoot内容协商机制(就是接受数据的类型如json,xml)

目录 一、基于请求头的内容协商机制 二、基于请求参数的内容协商机制 一、基于请求头的内容协商机制 如果我们的Java服务为浏览器和安卓手机同时提供服务&#xff0c;浏览器期望接受的请求是JSON格式&#xff0c;安卓客户端期望接收的请求是XML格式&#xff0c;这个时候是否需…

CentOS-7安装clickhouse并允许其他主机登录

一、通用设置 1、配置主机名 hostnamectl set-hostname --static 主机名2、修改hosts文件 vim /etc/hosts 输入&#xff1a; 192.168.15.129 master 192.168.15.133 node1 192.168.15.134 node2 192.168.15.136 node33、 保持服务器之间时间同步 yum install -y ntpdate &…

互联网和嵌入式,哪个更吃香?

在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;显然&#xff0c;互联网更受青…

Vue3+Echarts: 浏览器缩小后,图表内容发生重叠

一、问题 Vue3Echarts项目&#xff1a;浏览器缩小后&#xff0c;图表内容发生重叠。本文将提供几个解决上述问题的思路&#xff0c;后续有新的解决思路将在此处进行补充。 二、解决思路 1、动态调整ECharts配置 如果图表容器的尺寸没有随着浏览器窗口的缩小而进行相应地调整…