标签:
1. Tomcat主要有两个组件,连接器和容器,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器。容器即servlet容器,容器有很多层,分别是Engine,
Host,Context,Wrapper。最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet。从连接器
传过来连接后,容器便会顺序经过上面的容器,最后到达特定的servlet。要说明的是Engine,Host两种容器在不是必须的。实际上一个简单的tomcat只要连接器和容器就可以了,
但tomcat的实现为了统一管理连接器和容器等组件,额外添加了服务器组件(server)和服务组件(service),添加这两个东西的原因我个人觉得就是为了方便统一管理连接器和
容器等各种组件。一个server可以有多个service,一个service包含多个连接器和一个容器,当然还有一些其他的东西,看下面的图就很容易理解Tomcat的架构了:
2. 一个父组件又可以包含多个子组件,这些被统一管理的组件都实现了Lifecycle接口。只要一个组件启动了,那么他的所有子组件也会跟着启动,比如一个server启动了,它的所有子
service都会跟着启动,service启动了,它的所有连接器和容器等子组件也跟着启动了,这样,tomcat要启动,只要启动server就行了,其他的组件都会跟随着启动
3. 一般启动Tomcat会是运行startup.bat或者startup.sh文件,实际上这两个文件最后会调用org.apache.catalina.startup.Bootstrap类的main方法,这个main方法主要做了两件事情,
1:定义和初始化了tomcat自己的类加载器,2:通过反射调用了org.apache.catalina.startup.Catalina的process方法;
4. process方法的功能也很简单,1:如果catalina.home和catalina.base两个属性没有设置就设置一下,2:参数正确的话就调用execute方法,execute的方法就是简单的调用start方法,
其中在判断参数正确的方法arguments中会设置starting标识为true,这样在execute方法中就能调用start方法,start方法是重点,在它里面启动了我们的Tomcat所有的服务
5. 这里最重要的方法是createStartDigester();和((Lifecycle) server).start();createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect,至于
怎么实例化的等下再看,start方法就是启动服务实例了。File file = configFile();是新建server.xml文件实例,后面的服务组件都是要根据这个文件来的
6. Digester是一个外部jar包里面的类,主要的功能就是解析xml里面的元素并把元素生成对象,把元素的属性设置成对象的属性,并形成对象间的父子兄弟等关系。
Digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");//创建一个org.apache.catalina.core.StandardServer对象,实际上这里并没有真正
创建出一个对象,而是添加一个模式,只是后面创建的对象是根据这些模式和server.xml来的,所以可以暂时这么理解。真正创建对象是在start方法里面的digester.parse(is),is是
server.xml文件的流,digester刚才已经添加了StandardServer和StandardService等服务组件,也添加了StandardServer和StandardService的关系以及StandardService和连接器
HttpConnector,容器StandardHost的关系,所以调用digester.parse(is)方法后就会根据模式和server.xml文件来生成对象以及他们之间的相互关系。这样我们便有了服务器组件
StandardServer的对象,也有了它的子组件StandardService对象等等
7. 既然有了服务器组件的对象,就初始化然后启动就可以了,到此,tomcat就实现了启动服务器组件StandardServer。启动后做的事情就东西比较多,但是还是比较清晰的,
StandardServer的start方法关键代码是启动它的子组件StandardService。StandardService的start方法跟StandardServer的start方法差不多,是启动它的连接器和容器,上面说了一个
Service包含一个容器和多个连接器
8. 默认的连接器是HttpConnector,所以会调用HttpConnector的start方法。这里有个两个关键的类:HttpConnector和HttpProcessor,它们都实现了Runnable接口,HttpConnector
负责接收http请求,HttpProcessor负责处理由HttpConnector接收到的请求。注意这里HttpProcessor会有很多的实例,最大可以有maxProcessor个,初始化是20个。所以在
threadStart方法中会启动一个后台线程来接收http连接
9. 这样,就会启动HttpConnector后台线程,它的run方法不断循环,主要就是新建一个ServerSocket来监听端口等待连接。serverSocket一直等待连接,得到连接后给HttpProcessor
的实例processor来处理,serverSocket则继续循环监听,至于processor具体怎么处理,还有很多要说,这里先不说。
Tomcat源码分析(二)--连接处理
1. 在上一节里已经启动了一个HttpConnector线程,并且也启动了固定数量的HttpProcessor线程。HttpConnector用来等待http连接,得到http连接后交给其中的一个HttpProcessor
线程来处理。接下里具体看一下HttpConnector是怎么得到连接得,以及HttpProcessor是怎么处理的
2. 这里很关键的就是socket = serverSocket.accept();和processor.assign(socket); 在循环里面内,serverSocket.accept();负责接收http请求然后赋值给socket,最后交给其中一个processor
处理。这里processor并不是等到需要的时候再实例化,而是在HttpConnector初始化的时候已经有了若干个processor。httpConnector里面持有一个包含HttpProcessor对象的栈,需要的
时候拿出来就是了。
3. 接下来由processor.assign(socket); 记住这个方法是异步的,不需要等待HttpProcessor来处理完成,所以HttpConnector才能不间断的传入Http请求
4. 很明显,在它的run方法一开始便是调用上面的await方法来等待(因为一开始available变量为false),所以HttpProcessor会一直阻塞,直到有线程来唤醒它。当从HttpConnector中调用
processor.assign(socket),会把socket传给此HttpProcessor对象,并设置available为true,调用notifyAll()唤醒该processor线程以处理socket。同时,在await方法中又把available
设置成false,因此又回到初始状态,即可以重新接受socket。这里处理socket的方法是process(socket),主要作用有两点,1:解析这个socket,即解析http请求,包括请求方法,请求协议等,以
填充request,response对象(是不是很熟悉,在servlet和jsp开发经常用到的request,response对象就是从这里来的)。2:传入request,response对象给和HttpConnector绑定的容器,让容器来
调用invoke方法进行处理。
5. 在那些parse××方法里面会对request,response对象进行初始化,然后调用容器的invoke方法进行处理,至此,http请求过来的连接已经完美的转交给容器处理,容器剩下的问题就是要最终转
交给哪个servlet或者jsp的问题。前面我们知道,一个连接会跟一个容器相连,一个级别大的容器会有一个或者多个子容器,最小的容器是Wrapper,对应一个servlet,在这里我们只要知道请求的
路径决定了最终会选择哪个wrapper,wrapper最终会调用servlet的。至少一开始提出来的问题已经明白了。
标签:
原文地址:http://www.cnblogs.com/Jtianlin/p/5041703.html