`

读《How To Tomcat Works》笔记

 
阅读更多

servlet容器是一个复杂的系统,不过,一个servlet容器要为一个servlet的请求提供服务,要有三件事要做:1.创建一个request对象并填充那些有可能被引用的servlet使用的信息,如参数,头部,cookies,查询字符串,URL等。一个request对象是javax.servlet.ServletRequestjavax.servlet.http.ServletRequest的一个实现。2.创建一个reponse对象,所引用的servlet使用它来给客户端发送响应。一个response对象javax.servlet.ServletReponsejavax.servlet.http.ServletReponse的一个实现.3.调用servletservice方法,并传入requestreponse对象。这里servlet会从request对象取值,给response写值。

一个HTTP请求包括三个组成部分:1.方法,同义资源标识符(URI),协议/版本2.请求的头部3.主题内容,例子

POST /examples/default.jsp HTTP/1.1

Accept: text/plain; text/html

Accept-Language: eb-gb

Connect:Keep-Alive

Host: localhost

User-Agent: Mozilla/4.0(compatible; MSIE 4.01; Windows 98)

Content-Type: application/x-www-form-urlencoded

Accept-Encoding: gzip, deflate

 

lastName=Frank&firstName=Michael

第一行是方法,同义资源标识符(URI),协议/版本,从第二行开始是请求的头部,直到一个回车换行,这是请求头部和请求主题的分隔,这个例子的最后一行是主题。

一个HTTP响应基本跟请求类似,只是内容不同,例子:

HTTP/1.1  200  OK

Server: Microsoft-IIS/4.0

Date:Mon, 5 Jan 2004 13:13:33 GMT

Content-Type: text/html

Last-Modified: Mon, 5 Jan 2004  13:13:12 GMT

Content-Length:112

 

<html>

<head>

<title>HTTP Response Example</title>

</head>

<body>

Welcome to Brainy Software

</body>

</html>

套接字(Socket)是网络连接的一个端点。Socket使得一个应用可以从网络中读取和写入数据。放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另一个应用,你需要知道另一个应用的IP地址和套接字端口。在JAVA里,Socket指的是java.net.SocketServerSocket类。

一个简单的web server实现步骤:

  1. 启动应用程序。即main方法,主要new出个connector
  2. 连接器。即connector,这主要就是ServerSocket的初始化,建立连接,然后将socket传给下步的Processor
  3. 处理器。即从socket中提取inputstreamoutputstream,分别封装给HttpRequest(实现HttpServletRequest)和HttpResponse(实现HttpServletResponse),从request中提取用户的请求,通过reponse发送回应的信息。这个过程中,具体的处理可能有静态资源处理(如静态HTML页面访问,直接传回),或servlet处理(根据request里请求的类,去找对应的类,然后这里load此类,并运行)等。

HTTP1.1加入的新特征:

  1. 持久连接。在HTTP1.1之前,无论什么时候浏览器连接到一个WEB服务器,当请求的资源被发送后,连接就被服务器关闭了。然而,一个互联网网页包括其他资源,例如图片,applet等。因此,当一个页面被请求的时候,浏览器同样需要下载加载页面所引用到的资源。加入页面和它所引用到的全部资源使用不同连接来下载的花,进程将会很慢,所以HTTP1.1加入了持久连接特性。使用持久连接的时候,当页面下载的时候,服务器并不直接关闭连接,相反,它等待WEB客户端请求页面所引用的全部资源。这样,页面和所引用的资源使用同一个连接来下载。持久连接是HTTP1.1默认的连接方式,在请求头有connection: keep-alive
  2. 块编码
  3. 状态100(持续状态)的使用

tomcat连接器必须实现org.apache.catalina.Connector接口。这个接口里的主要方法是getContainer,setContainer,createRequest,createResponse.org.apcahe.catalina.connector.http.HttpConnectorConnector接口的一个实现。HttpConnector实现了Connector,Runnable(因此可以它的实例可以运行在自己的线程上),Lifecycle。在创建HttpConnector后,应该调用他的initializestart方法,而且只能一次。实现中的initialize方法主要初始化了个ServerSocket实例(通过一个服务器套接字工厂提供实例)。在HttpConnector运行时(因为其实现了Runnable),其run的方法里,通过一个while循环,不断的去监听是否有客户端socket到来,有的话分别产生一个HttpProcessor实例(这也是实现了Runnable的),每一个请求都有一个线程产生即HttpConnector实例产生来处理具体的请求(通过一个Vector维护这个线程池,有一个最大数限制)。HttpProcessor里具体的process会做以下几个事情:解析连接,解析请求,解析头部。

对于Container,容器必须继承org.apache.catalina.Container接口。在catalina中,一共有四种不同的容器:

  1. Engin: 表示整个catalinaservlet引擎
  2. Host: 表示一个拥有数个上下文的虚拟主机
  3. Context: 表示一个WEB应用,一个context包含一个或多个wrapper
  4. Wrapper:表示一个独立的servlet

Engin, Host, Context, Wrapper接口的标准实现是StandardEngin, StandardHost, StandardContext, StandardWrapper,它们都在org.apache.catalina.core包中,四个标准实现从前向后的关系,都是一对多。

org.apache.catalina中有四个跟流水线任务处理相关的接口:Pipeline, Valve, ValveContextContained.一个pipeline包含了该容器要唤醒的所有任务。每个阀门表示一个特定的任务。一个容器的流水线有一个基本的阀门,但是你可以添加任意你想要添加的阀门。一个流水线就像一个过滤链,每个阀门就像一个过滤器。跟过滤器一样,一个阀门可以操作传递给它的requestresponse方法。让一个阀门完成处理,则进一步处理流水线中的下一个阀门,基本阀门总是在最后才能调用。具体实现上就是遍历所有valve,一次调用其invoke,这个是放在ValveContext里实现的。

Pipeline可以增加或删除阀门valve,调用invoke(request, response)开始唤醒流水线的阀门。

一个servlet容器需要一个定制的容器,而不是简单的使用系统的加载器。如果使用系统的加载器来加载servlet和其他类,这样servlet就可以进入java虚拟机classpath环境下的任何类和类库,这样会带来安全隐患。servlet只允许访问WEB-INF/目录下的类以及部署在WEB-INF/lib目录下的类库。所以一个servlet容器需要一个自己的加载器,该加载器遵守一些特定的规则来加载类。在Catalina中,加载器使用org.apache.catalina.Loader接口表示。Tomcat需要一个自己的加载器的另一个原因是它需要支持在WEB-INF/classes或者是WEB-INF/lib目录被改变的时候会重新加载。Tomcat的加载器实现中使用一个单独的线程来检查servlet和支持类文件的时间戳。要支持类的自动加载功能,一个加载器类需要实现org.apache.catalina.loader.Reloader接口

WebappLoader支持自动重载,如果WEB-INF/classes或者是WEB-INF/lib目录被重新编译过,在不启动Tomcat的情况下必须自动重新载入这些类。为了实现这个目的,WebappLoader有一个单独的线程每个x秒会检查源的时间戳。x的值默认是15,即每15秒进行一次检查是否需要自动重载。

当加载一个类的时候,WebappClassLoader类遵循以下规则:

所有加载过的类都要进行缓存,所以首先需要检查本地缓存。

如果无法在本地缓存找到类,使用java.lang.ClassLoader类的finaLoaderClass方法在缓存查找类。

如果在两个缓存中都无法找到该类,使用系统的类加载器避免从J2EE类中覆盖来的web应用程序。

如果使用了安全管理器,检查该类是否允许加载,如果该类不允许加载,则抛出ClassNotFoundException异常

如果要加载的类使用了委托标志或该类属于trigger包中,使用父加载器来加载类,如果父加载器为null,使用系统加载器加载。

从当前的源中加载类

如果在当前的源中找不到该类并且没有使用委托标志,使用父类加载器。如果父类加载器为null,使用系统加载器。

如果该类仍然找不到,抛出ClassNotFoundException异常。

对于session,必须实现javax.servlet.http.HttpSession接口,tomcat的实现有StandardSession,里面主要有个hashmap的属性存放sessionkeyvaluetomcat还有个管理器org.apache.catalina.Manager接口,其用来管理session对象。例如它创建session对象并销毁它们。具体实现有StandardManagerPersistentManagerBase,在运行时,StandardManagersession对象存放在内存中,但是,当停止的时候,它将session对象存放在文件中。当它再次启动的时候,重新载入session对象。

StandardWrapper里有个allocate的方法,主要是对servlet(Servlet的实例)如果已经有实例话的话,就直接返回这个instance(已经是作为一个属性存下来的了),而如果instancenull,则会调用loadServlet方法开始实例化对应的servlet,这时如果是个jsp会先转化了servlet在实例化。

StandardWrapperValveStandardWrapper实例上的基本阀门,该阀门做两件事情:

  1. 提交servlet的所有相关过滤器
  2. 调用发送者的service方法

要实现这些内容是调用StandardWrapperValveinvoke方法要实现的:

  1. 调用StandardWrapperallocate的方法获得一个servlet实例
  2. 调用它的private createFilterChain方法获得过滤器
  3. 调用过滤器的doFilter方法。包括调用servletservice方法
  4. 释放过滤器
  5. 调用包装器的deallocate方法
  6. 如果servlet无法使用了,调用包装器的unload方法。

 

一个上下文容器(Context)代表一个web应用,每一个上下文包括多个包装器(Wrapper),每个包装器代表一个Servlet.创建一个StandardContext实例之后,必须调用它的start方法,这样它就能接受HTTP请求服务了。在start启动后,StandardContext对象需要配置它的属性。在一个tomcat部署中,StandardContext的配置过程做了以下事情:准备读取和解析%CATALINA_HOME%/conf目录下面的web.xml,部署所有应用程序,确保StandardContext实例可以处理应用级别的web.xml. StandardContext定义了reloadable属性来标识是否支持应用程序的重加载。当允许重加载的时候,当web.xml或者WEB-INF/classes目录下的文件被改变的时候会重加载。

如果需要在一个tomcat部署中部署多个上下文,理论上,当只有一个上下文容器的时候不需要主机。但是实践中,一个tomcat部署往往需要一个主机。引擎标识整个Catalinaservlet引擎,如果使用的话,它位于容器等级的最高层。在一个tomcat部署中,默认的容器是引擎,在该部署中,引擎只有一个主机,默认主机。

Server是标识整个catalina servlet容器的一个整个实现的接口,StandardServer是其的标准实现,其initialize方法是初始化要添加到服务器实例上的服务,start方法是启动一个服务器,它将启动所有服务及其相关组件,例如连接器和容器,stop方法用于停止一个服务器,其await方法负责整个tomcat部署的停止机制,他一直运行直到接到客户端的停止请求并停止tomcat

Server表示服务,一个服务可以有一个容器和多个连接器。你可以添加多个连接器,并将它们跟容器相关联。StandardServer实例包括两种组件:一个容器和多个连接器。多个连接器可以使得tomcat能服务于多个协议。一个协议用于处理HTTP请求,另一个用于处理HTTPS请求。

DigesterAoache Jakarta项目下的开源项目,他提供了基于规则的任意XML文档处理的实现,tomcat使用他来将XML元素转换为JAVA对象。

JAVA中,虚拟机遇到两种事件的时候会关闭虚拟机:

  1. 应用程序正常推出如System.exit方法被调用或者最后一个非守护退出
  2. 用户突然强制终止虚拟机,例如键入CTRLC或者在关闭JAVA程序之前从系统注销。

在虚拟机退出时,尤其是后一种,强制退出往往会发生一些意外的结果,幸运的是JAVA提供了一种优雅的方式来帮程序员处理即关闭钩子。而虚拟机在关闭时也会执行以下两个步骤:

  1. 虚拟机启动所有注册的关闭钩子。关闭钩子是实现在Runtime上面注册的线程。所有的关闭钩子会被同时执行直到完成。
  2. 虚拟机调用所有的未被调用的finalizers

我们可以通过第一步,它允许虚拟机提交清理代码。一个关闭钩子是Thread类的子类,可以如下创建一个关闭钩子:

  1. 写一个类继承Thread
  2. 提供你的实现类中的run方法,该方法是应用程序被关闭的时候要提交的代码,无论是正常退出还是非正常退出
  3. 在你的应用程序中,初始化一个关闭钩子
  4. 在当前的Runtime上使用addShutdownHook方法来注册该关闭钩子。

这里没有启动线程,是虚拟机在它的关闭步骤中会启动该线程。

tomcat的启动类是org.apache.catalina.startup.Catalina.它包含一个用于解析%CATALINA_HOME%/conf目录下面server.xml文件的Digester.理解了如何往该Digester添加规则,你可以根据你的想法来配置该tomcat.Catalina类还封装了一个Server对象来提供服务(service),一个service包括一个容器以及一个或多个连接器,可以使用Catalina来启动或停止Server对象。Catalina有个main方法就是入口,他先实例化个Catalina,调用process方法启动tomcat,在调用该方法时必须传递合适的参数。process在处理完一些参数(系统参数,还有是通过Digester解析的server.xml的配置)后开始server的启动。

org.apache.catalina.startup.Bootstrap类提供了外界启动tomcat的真正入口,当你运行startup.bat或者是startup.sh的时候,实际上运行的就是这个类的main方法,他主要就是加载Catalina类,然后调用Catalinaprocess方法。

要使得一个web应用可以访问,一个上下文必须先部署在主机上。在tomcat中,一个上下文可以以WAR文件的形式部署,也可以直接将整个应用程序部署在tomcat安装目录的webapp目录下面。对你部署的每个应用,都可以有一个配置脚本用来配置该上下文,配置脚本以XML文档的形式存在。

部署一个web上下文,主要是StandardHost实例的start方法启动的时候,他会触发start事件,HostConfig的响应是它会调用它自己的start方法,它会部署和安装所有的特定目录下面的web应用程序。

真正的deploy动作主要有几种,war filedeploy : HostConfig里的deployWARs,直接一个目录的部署:HostConfig里的deployDirectories,还有一种是live deploy,他是单独分配一个线程运行,周期性的检查在web.xml文件中的已存在部署是否有改变,默认是15秒一次。

部署器用于部署和安装web应用,由org.apache.catalina.Deploy表示。StandardHost类实现了Deployer,这样他就是一个可以部署web应用的特殊容器。StandardHost使用了一个帮助类完成web应用的部署和安装,即StandardHostDeploy,该类提供了部署和安装应用,以及启动和停止上下文容器的代码。

JMX在内部管理方面提供了很大的灵活性,很多基于服务的应用程序,如tomcat,jboss,JONAS以及其他的应用,都使用JMX来管理它们的服务。JMX定义了一个公开的管理JAVA对象的标准,例如,tomcat45使用JMX的管理程序来使得各种对象(如服务器,主机,上下文,阀门等等)可用。tomcat的开发者编写了admin应用程序用于管理。一个可以使用JMX管理器来管理的JAVA对象称为JMX管理资源。事实上,一个JMX管理资源也可以是一个应用程序,一个实现或者一个服务,设备,用户等等。JMX管理资源用JAVA写或者提供一个JAVA包装。要想让一个JAVA对象称为JMX管理资源,必须创建另一个名为Managed Bean或者MBean的对象。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics