tomcat
以下代码以XML的形式展示了各种组件之间的关系:
<Server> 顶层类元素,可包含多个service
<Service> 顶层类元素,可包含一个Engine和多个Connector
<Connector/> 链接类容器,代表通信接口
<Engine> 容器元素,为Service处理客户请求,含多个Host
<Host> 容器元素,为Host处理客户请求,含多个Context
<Context/> 容器元素,为Web应用处理客户请求
</Host>
</Engine>
</Service>
</Server>
核心组件是Catalina Servlet容器,它是所有其他Tomcat组件的顶层容器。代码中每个元素都代表一种 Tomcat 组件。这些元素可分为4类。
1、顶层类元素:包括<Server>元素和<Service>元素,它们位于整个配置文件的顶层。Server表示整个的Catalina Servlet容器。
2、连接器类元素:代表了介于客户与服务之间的通信接口,负责将客户的请求发送给服务器,并将服务器的响应结果传递给客户。
3、容器类元素:代表处理客户请求并生成响应结果的组件,有3种容器类元素,它们是Engine、Host和Context。Engine组件为特定的 Service组件处理所有的客户请求,Host组件为特定的虚拟主机处理所有客户请求,Context组件为特定的Web应用处理所有客户请求。
4、嵌套类元素:代表了可以加入到容器中的组件,如<Logger>元素、<Valve>元素和 <Realm>元素。
Tomcat各组件件的嵌套关系
Tomcat Server的结构图
Tomcat Server处理一个http请求的过程
假设来自客户的请求为:
http://localhost:8080/wsota/wsota_index.jsp
1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得
2) Connector把该请求交给processor,它会建立input,output,并由此组建response和request,然后将组建的response和request传给Service的Engine来处理,并等待来自Engine的回应
3) Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host
4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
5) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context
6) Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
7) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet
8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,并通过类加载器载入类
9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
10)Context把执行完了之后的HttpServletResponse对象返回给Host
11)Host把HttpServletResponse对象返回给Engine
12)Engine把HttpServletResponse对象返回给Connector
13)Connector把HttpServletResponse对象返回给客户browser
/bin:包含启动和关闭Tomcat的文件
/conf:包含不同的配置文件:server.xml,web.xml,tomcat-user.xml
/lib:包含Tomcat使用的JAR文件
/logs:包含日志文件
/webapps:包含应用程序示例及自己开发的程序
/work:包含有JSP生成的Servlet
(四)tomcat启用过程分析
一般启动Tomcat会是运行startup.bat或者startup.sh文件,实际上这两个文件最后会调用org.apache.catalina.startup.Bootstrap类的main方法,
这个main方法主要做了两件事情,1:定义和初始化了tomcat自己的类加载器,2:通过反射调用了org.apache.catalina.startup.Catalina process方法
process的功能1:如果catalina.home和catalina.base两个属性没有设置就设置一下,2:参数正确的话就调用execute方法,execute的方法就是简单的调用start方法,其中在判断参数正确的方法arguments中会设置starting标识为true,这样在execute方法中就能调用start方法,start方法是重点,在它里面启动了我们的Tomcat所有的服务。
start方法功能:最重要的方法是createStartDigester();和((Lifecycle) server).start(); createStartDigester方法主要的作用就是帮我们实例化了所有的服务组件包括server,service和connect,start方法就是启动服务实例了。
Digester是一个外部jar包里面的类,主要的功能就是解析xml里面的元素并把元素生成对象,把元素的属性设置成对象的属性,并形成对象间的父子兄弟等关系。调用digester.parse(is)方法后就会根据模式和server.xml文件来生成对象以及他们之间的相互关系。这样我们便有了服务器组件StandardServer的对象,也有了它的子组件StandardService对象等等。
Start执行到server.initialize(); ((Lifecycle) server).start();tomcat就实现了启动服务器组件StandardServer。StandardServer的start方法关键代码是启动它的子组件StandardService,StandardService的start方法跟StandardServer的start方法差不多,是启动它的连接器和容器,一个Service包含一个容器和多个连接器。
默认的连接器是HttpConnector,所以会调用HttpConnector的start方法。有两个关键的类:HttpConnector和HttpProcessor,它们都实现了Runnable接口,HttpConnector负责接收http请求,HttpProcessor负责处理由HttpConnector接收到的请求。注意这里HttpProcessor会有很多的实例,最大可以有maxProcessor个,初始化是20个。所以在threadStart方法中会启动一个后台线程来接收http连接。这样,就会启动HttpConnector后台线程,它的run方法不断循环,主要就是新建一个ServerSocket来监听端口等待连接,得到连接后给HttpProcessor的实例processor来处理,serverSocket则继续循环监听。在循环里面内,serverSocket.accept()负责接收http请求然后赋值给socket,最后交给其中一个processor处理。这里processor并不是等到需要的时候再实例化,而是在HttpConnector初始化的时候已经有了若干个processor放入了Stack中,每次createProcessor时从stack中pop出一个就可以。HttpProcessor的run方法一开始便是调用await方法来等待。所以HttpProcessor会一直阻塞,直到有线程来唤醒它。当从HttpConnector中调用processor.assign(socket),会把socket传给此HttpProcessor对象,并设置available为true,调用notifyAll()唤醒该processor线程以处理socket。同时,在await方法中又把available设置成false,因此又回到初始状态,即可以重新接受socket。
处理socket的方法是process(socket),主要作用有两点,
1:解析这个socket,即解析http请求,包括请求方法,请求协议等,以填充request,response对象。
2:传入request,response对象给和HttpConnector绑定的容器,让容器来调用invoke方法进行处理。
Connector 处理一次请求顺序图