tomcat4 请求的处理——初步分析

时间:2023-01-05 07:23:59

以tomcat4为例,

每当HttpConnector的ServerSocket得到客户端的连接时,会创建一个Socket。

接下来就处理这个Socket发来的数据。 怎么处理呢?

考虑到客户端同时发来的请求数可能有很多, 所以tomcat 中默认维护着一个连接池—— 最大数是:..

这里的连接池可以理解为处理池, 因为它维护的是HttpProcessor 的List之类的。。

——————

这里的处理池里面的所有HttpProcessor 是一直是运行之中的!! 就是一创建就处于了不断运行之中的。 —— 更具体的时机暂不清楚, 应该是创建池的时候就同时把他们都创建好了的。

—— 这里的运行, 是指没有死掉的意思, 其状态可能是Running、Waiting、Sleeping,(运行、阻塞、等待、休眠、。。) 而不仅仅表示Running

—— 之所以它会一直运行, 是因为它run 方法包含了一个无线循环(此处不能叫做死循环, 因为它有可能被唤醒的!!)的原因.

此循环, 依据一定的条件被唤醒,然后工作,工作完了,又回到休息或者等待状态! 具体见后面分析。

——————

每一个HttpProcessor 是一个线程—— 明白这点非常重要, 这也是我们把HttpProcessor 池成为线程池的原因!!

——————

首先, 从处理池中获取一个连接HttpProcessor (此时的HttpProcessor 一定的处于Waiting状态的!!),然后传给它刚刚获得的socket,让他进行处理。

—— 具体又是怎么让它进行处理的呢?

首先,要唤醒它,通过notify!

然后,使用socket处理(  此处的socket是局部变量——————  )当前 线程会从主线程拷贝一份。。。 ———— 这个具体有点不清楚!!

然后,处理完了,改变标志,又回到循环中(继续休息或者说等待!)!

然后,此HttpProcessor 被回收到池中,表明它可以被下一个客户端的建立连接了!!

明白了!

其实,关键点在于HttpProcessor 写得很巧妙, 继承Runnable, 又使用了线程的wait、notifyAll特性。

-- 等等, 为什么使用notifyAll呢? 。。

————————————

如果是我来写tomcat, 我应该怎么做呢? 或者说, 为什么tomcat一定要这么一种方式来设计呢??

其实是有原因的! 就是为了提供效率!

试想, 我们写了一个服务端, 我们为每一个客户端请求建立连接,进行处理,

—— 由于我们的服务端的端口只有一个(多个的情况以后再说), 所以, 我们对所有的客户端请求, 都必须在这一个服务端主服务类的代码内完成。

最开始,我们为每一个客户端请求建立连接(这当然是必须的,不可省略的),且对所有请求在一个线程内完成——这样的特点是,服务端的内存占用小,服务端很忙,服务端响应慢。但是, 客户端请求一变多,客户端的等待马上变得非常久了—— 这种方案马上被否定。

于是,为了快速的响应,我们为每一个客户端请求建立连接,并安排一个新的线程去处理它,(所有的线程都肯定的要占用一定的内存。。并且假设里面没有无限循环,即处理就退出, 但是它也是需要时间去处理的!)—— 当来自客户端请求在短时间内过大的时候, 服务端内存吃不消了!—— 甚至可能,服务端的临时端口(通过ServerSocket accept生成的那个)都不够了!

这样不行, 于是,我们也设立了线程池,—— 连接池的本质上是线程池。—— 先是一启动服务器的时候就建立了很多线程(放在池中)进行等待客户端的请求。客户端的请求一来,马上分配一个线程(我们可以把它称为处理线程,或处理器线程,一个短时的处理客户端请求的线程)进行处理之,处理完了之后回收到池中。—— 同时,需要记住的一点是, 这些线程都是一直在运行中的!

那么问题来了:

主线程(就是服务器工作主线程) 分配处理线程socket的时候,怎么告诉他,socket已经分配了, 你工作吧!—— 就是唤醒。

—— 或者有人会说, 我分配socket的时候,给处理线程的相应变量赋值了,处理线程监听其相应socket变量,看是否有值,有就运行,没有等待,不就可以了吗? —— 我不知道是否行得通。———— 问题是,主线程和处理线程可以这样的直接通信吗?
但,实际情况下,我们可能还是必须要使用到线程间的通信机制。

—— 这样,tomcat4的连接器、处理器的代码逻辑就变得自然而然了。。。