博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomcat源码解读系列(三)——Tomcat对HTTP请求处理的整体流程
阅读量:6437 次
发布时间:2019-06-23

本文共 3883 字,大约阅读时间需要 12 分钟。

hot3.png

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

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

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的请求处理方式。

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方法,该方法会生成或者在线程池中取到一个线程。

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);                }            }        }    }

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

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,在此后的处理中,会有一些请求的预处理,我们用一个时序图来表示一下:

220236_RQGN_1417419.png

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

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

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

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

220617_Yvtt_1417419.png

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

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

220936_NBIF_1417419.png

221018_aH3Q_1417419.png

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

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

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

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

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

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

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

转载于:https://my.oschina.net/heroShane/blog/197664

你可能感兴趣的文章
远程桌面退出全屏/不能全屏/全屏切换的技巧
查看>>
【Java】Float计算不准确
查看>>
mybatis在xml文件中处理大于号小于号的方法
查看>>
Codeforces Codeforces Round #319 (Div. 2) A. Multiplication Table 水题
查看>>
各大浏览器CSS Hack收集
查看>>
再谈 $* 和 $@ 在 Bash 中的表现
查看>>
Apache Commons工具集简介
查看>>
Win8Metro(C#)数字图像处理--2.33图像非线性变换
查看>>
【翻译】Nginx的反向代理
查看>>
htm、html、shtml网页区别
查看>>
SpringCloud学习笔记:服务注册与发现Eureka(2)
查看>>
学习新 api 的思考过程 4.18
查看>>
想要设计自己的微服务?看这篇文章就对了
查看>>
一起撸个朋友圈吧(step5) - 控件篇【评论控件优化】
查看>>
一起撸个朋友圈吧 图片浏览(上)【图片点击前景色】
查看>>
[译] 原生 JavaScript 值得学习吗?答案是肯定的
查看>>
29岁了还一事无成是人生的常态?
查看>>
gRPC-rs:从 C 到 Rust
查看>>
Mysql-高性能索引
查看>>
chrome浏览器最小字号解决方案
查看>>