声明:源码版本为Tomcat 6.0.35

前面的文章中介绍了Tomcat初始化的过程,本文将会介绍Tomcat对HTTP请求的处理的整体流程,更细节的。

在上一篇文章中,介绍到JIoEndpoint 中的内部类Acceptor用来接受Socket请求,并调用processSocket方法来进行请求的处理,所以会从本文这个方法开始进行讲解。

1
2
3
4
5
6
7
8
9
10
11
12
protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            else {
                executor.execute(new SocketProcessor(socket));
            }
        catch (Throwable t) {
            //……此处略去若干代码
        }
        return true;
    }

在以上的代码中,首先会判断是否在server.xml配置了进程池,如果配置了的话,将会使用该线程池进行请求的处理,如果没有配置的话将会使用JIoEndpoint中自己实现的线程池WorkerStack来进行请求的处理,我们将会介绍WorkerStack的请求处理方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected Worker getWorkerThread() {
        // Allocate a new worker thread
        synchronized (workers) {
            Worker workerThread;
            while ((workerThread = createWorkerThread()) == null) {
                try {
                    workers.wait();
                catch (InterruptedException e) {
                    // Ignore
                }
            }
            return workerThread;
        }
    }

在以上的代码中,最终返回了一个Worker的实例,有其来进行请求的处理,在这里,我们再看一下createWorkerThread方法,该方法会生成或者在线程池中取到一个线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected Worker createWorkerThread() {
        synchronized (workers) {
            if (workers.size() > 0) {
            //如果线程池中有空闲的线程,取一个
                curThreadsBusy++;
                return workers.pop();
            }
            if ((maxThreads > 0) && (curThreads < maxThreads)) {
                //如果还没有超过最大线程数,会新建一个线程
                curThreadsBusy++;
                return (newWorkerThread());
            else {
                if (maxThreads < 0) {
                    curThreadsBusy++;
                    return (newWorkerThread());
                else {
                    return (null);
                }
            }
        }
    }

到此,线程已经获取了,接下来,最关键的是调用线程实现Worker的run方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void run() {
            // Process requests until we receive a shutdown signal
            while (running) {
                // Wait for the next socket to be assigned
                Socket socket = await();
                if (socket == null)
                    continue;
                   if (!setSocketOptions(socket) || !handler.process(socket)) {
                    try {
                        socket.close();
                    catch (IOException e) {
                    }
                }
                socket = null;
                recycleWorkerThread(this);
            }
        }

这里跟请求处理密切相关的是handler.process(socket)这一句代码,此处handle对应的类是Http11Protocol中的内部类Http11ConnectionHandler,在此后的处理中,会有一些请求的预处理,我们用一个时序图来表示一下:

在这个过程中,会对原始的socket进行一些处理,到CoyoteAdapter时,接受的参数已经是org.apache.coyote.Request和org.apache.coyote.Response了,但是要注意的是,此时这两个类并不是我们常用的HttpServletRequest和HttpServletResponse的实现类,而是Tomcat内部的数据结构,存储了和输入、输出相关的信息。值得注意的是,在CoyoteAdapter的service方法中,会调用名为postParseRequest的方法,在这个方法中,会解析请求,调用Mapper的Map方法来确定该请求该由哪个Engine、Host和Context来处理。

在以上的信息处理完毕后,在CoyoteAdapter的service方法中,会调用这样一个方法:

1
connector.getContainer().getPipeline().getFirst().invoke(request, response);

这个方法会涉及到Tomcat组件中的Container实现类的重要组成结构,即每个容器类组件都有一个pipeline属性,这个属性控制请求的处理过程,在pipeline上可以添加Valve,进而可以控制请求的处理流程。可以用下面的图来表示,请求是如何流动的:

可以将请求想象成水的流动,请求需要在各个组件之间流动,中间经过若干的水管和水阀,等所有的水阀走完,请求也就处理完了,而每个组件都会有一个默认的水阀(以Standard作为类的前缀)来进行请求的处理,如果业务需要的话,可以自定义Valve,将其安装到容器中。

后面的处理过程就比较类似了,会按照解析出来的Engine、Host、Context的顺序来进行处理。这里用了两张算不上标准的时序图来描述这一过程:

在以上的流程中,会一层层地调用各个容器组件的Valve的invoke方法,其中StandardWrapperValve这个标准阀门将会调用StandardWrapper的allocate方法来获取真正要执行的Servlet(在Tomcat中所有的请求最终都会映射到一个Servlet,静态资源和JSP也是如此),并按照请求的地址来构建过滤器链,按照顺序执行各个过滤器并最终调用目标Servlet的service方法,来完成业务的真正处理。

以上的处理过程中,涉及到很多重要的代码,后续的文章会择期要者进行解析,如:

  • Mapper中的internalMapWrapper方法(用来匹配对应的Servlet)

  • ApplicationFilterFactory的createFilterChain方法(用来创建该请求的过滤器链)

  • ApplicationFilterChain的internalDoFilter方法(用来执行过滤器方法以及最后的Servlet)

  • Http11Processor中的process方法、prepareRequest方法以及prepareResponse方法(用来处理HTTP请求相关的协议、参数等信息)

至此,我们简单了解一个请求的处理流程。

tomcat 解析(四)-处理http请求过程的更多相关文章

  1. TOMCAT原理详解及请求过程(转载)

    转自https://www.cnblogs.com/hggen/p/6264475.html TOMCAT原理详解及请求过程 Tomcat: Tomcat是一个JSP/Servlet容器.其作为Ser ...

  2. TOMCAT原理详解及请求过程

    Tomcat: Tomcat是一个JSP/Servlet容器.其作为Servlet容器,有三种工作模式:独立的Servlet容器.进程内的Servlet容器和进程外的Servlet容器. Tomcat ...

  3. Tomcat学习(二)------Tomcat原理详解及请求过程

    Tomcat: Tomcat是一个JSP/Servlet容器.其作为Servlet容器,有三种工作模式:独立的Servlet容器.进程内的Servlet容器和进程外的Servlet容器. Tomcat ...

  4. tomcat 解析(二)-消息处理过程

    接下来我们应该去了解一下 tomcat 是如何处理jsp和servlet请求的. 1.  我们以一个具体的例子,来跟踪TOMCAT, 看看它是如何把Request一层一层地递交给下一个容器, 并最后交 ...

  5. Tomcat Server处理一个http请求过程

    假设来自客户端的请求为: http://localhost:8080/lizhx/lizhx_index.jsp 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Conne ...

  6. 解析Tomcat内部结构和请求过程

    Tomcat Tomcat的组织结构 由Server.xml的结构看Tomcat的体系结构 Tomca的两大组件:Connecter和Container Connecter组件 Container组件 ...

  7. 网站开发进阶(四)Tomcat Server处理一个http请求的过程

    Tomcat Server处理一个http请求的过程 假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp 1) 请求被发送到本机端口8080 ...

  8. tomcat 解析(五)-Tomcat的核心组成和启动过程

    声明:源码版本为Tomcat 6.0.35 前面的文章中介绍了Tomcat的基本配置,每个配置项也基本上对应了Tomcat的组件结构,如果要用一张图来形象展现一下Tomcat组成的话,整个Tomcat ...

  9. WEB请求过程(http解析,浏览器缓存机制,域名解析,cdn分发)

    概述 发起一个http请求的过程就是建立一个socket通信的过程. 我们可以模仿浏览器发起http请求,譬如用httpclient工具包,curl命令等方式. curl "http://w ...

  10. 走进JavaWeb技术世界5:初探Tomcat的HTTP请求过程

    初探Tomcat的HTTP请求过程 前言:1.作为Java开发人员,大多都对Tomcat不陌生,由Apache基金会提供技术支持与维护,因为其免费开源且易用,作为Web服务器深受市场欢迎,所以有必要对 ...

随机推荐

  1. javascript a=a||"" 表达式

    其实: a=a||"defaultValue"; 与: if(!a){ a="defaultValue"; } 和: if(a==null||a==" ...

  2. 基础学习总结(八)--Intent中显示意图和隐式意图的用法

    Intent(意图)主要是解决Android应用的各项组件之间的通讯.Intent负责对应用中一次操作的动作.动作涉及数据.附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组 ...

  3. 【原】Infragistics.Win.UltraWinGrid.UltraGrid 增加行号

    private void ultraGrid1_InitializeLayout(object sender, Infragistics.Win.UltraWinGrid.InitializeLayo ...

  4. 从0零开始学slatstack-(0)在centos 6.5 安装 python2.7 salt

    由于服务器环境问题,我的一个小玩意失败了,于是下决心学习下saltstack来弄个好使的自动化配置管理工具.之所以不考虑puppet等,主要原因是我不熟ruby,深入学习困难.再其次,为什么考虑2.7 ...

  5. 基于Redis主从复制读写分离架构的Session共享

    1.搭建主从复制 第一步:将Redis拷贝到虚拟机上的指定文件夹内,此Redis作为主服务 第二步:将Redis拷贝到本机的指定文件夹内,此Redis作为从服务 第三步:修改主服务的配置文件(redi ...

  6. Sorl之java操作

    参考教程: http://www.cnblogs.com/xia520pi/p/3625232.html http://www.cnblogs.com/hujunzheng/p/5647896.htm ...

  7. How to begin Python learning?

    如何开始Python语言学习? 1. 先了解它,Wiki百科:http://zh.wikipedia.org/zh-cn/Python 2. Python, Ruby等语言来自开源社区,社区的学法是V ...

  8. 帝国cms刷洗内容页提示.phome_ecms_news_data_' doesn't exist

    帝国cms后台刷新提示.phome_ecms_news_data_' doesn't exist解决方法: 刷新所有信息内容页面时提示“Table '*.phome_ecms_article_data ...

  9. 网络笔记01-2 scoket

    scoket: 1.socket /** 第一个参数(domain): 表示用什么协议 AF_INET 为IPV4开发 第二个参数(type): 表示scoket为什么类型SOCK_STREAM为TC ...

  10. MYSQL远程登录权限设置

    Mysql默认关闭远程登录权限,如下操作允许用户在任意地点登录: 1. 进入mysql,GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY ...