logo资料库

Tomcat运行机制与源码分析.pdf

第1页 / 共367页
第2页 / 共367页
第3页 / 共367页
第4页 / 共367页
第5页 / 共367页
第6页 / 共367页
第7页 / 共367页
第8页 / 共367页
资料共367页,剩余部分请下载后查看
How Tomcat Works 中文版 介绍 概要 欢迎阅读《How Tomcat Works》这本书。这本书解剖了 Tomcat4.1.12 和 5.0.18 版本,解释 了它的 servlet 容器的内部运行机制,那是一个免费的,开源的,最受欢迎的 servlet 容器,代 号为 Catalina。Tomcat 是一个复杂的系统,由许多不同的组件构成。那些想要学习 Tomcat 运行 机制的朋友大部分知道从何入手。这本书会提供一个蓝图,然后为每一个组件构造一个简化版本, 使得可以更加容易的理解这些组件。在这之后才会对真实的组件进行解释。 你应该从这份简介开始阅读,因为它解释了这本书的结构,同时给你勾画了这个项目构造的 简洁轮廓。“准备前提软件”这一节会给你一些指示,例如你需要下载什么样的软件,如何为你 的代码创建目录结构等等。 本书为谁而作 这本书是为任何一个使用 Java 技术进行工作的人而准备的。  假如你是一个 servlet/jsp 程序员或者一个 Tomcat 用户,而且对一个 servlet 容器是 如何工作这个问题你感兴趣的话,这本书就是为你准备的。  假如你想加入 Tomcat 的开发团队的话,这本书就是为你准备的,因为你首先需要学习 那些已存在的代码是如何工作的。  假如你从未涉及 web 开发,但你对一般意义上的软件开发感兴趣的话,你可以在这本书 学到一个像 Tomcat 一样的大型项目是如何进行设计和开发的。  假如你想配置和自定义 Tomcat,你也应该读读这本书。 为了理解书中的讨论,你需要了解 Java 面向对象编程技术以及 servlet 编程。假如你对这 些不熟悉的话,这里有很多书籍可以参考,包括 Budi 的《Java for the Web with Servlets, JSP, and EJB》。为了让这些材料更容易理解,每一章开始都会有便于理解所讨论主题的必要的背景资 料介绍。 Servlet 容器是如何工作的 servlet 容器是一个复杂的系统。不过,一个 servlet 容器要为一个 servlet 的请求提供服 务,基本上有三件事要做:  创建一个 request 对象并填充那些有可能被所引用的 servlet 使用的信息,如参数、头 部、cookies、查询字符串、URI 等等。一个 request 对象是 javax.servlet.ServletRequest 或 javax.servlet.http.ServletRequest 接口的一个实 例。  创建一个 response 对象,所引用的 servlet 使用它来给客户端发送响应。一个 response 对象 javax.servlet.ServletResponse 或 javax.servlet.http.ServletResponse 接口 的一个实例。
 调用 servlet 的 service 方法,并传入 request 和 response 对象。在这里 servlet 会 从 request 对象取值,给 response 写值。 当你读这些章节的时候,你将会找到关于 catalina servlet 容器的详细讨论。 Catalina 架构图 Catalina 是一个非常复杂的,并优雅的设计开发出来的软件,同时它也是模块化的。基于 “Servlet 容器是如何工作的”这一节中提到的任务,你可以把 Catalina 看成是由两个主要模 块所组成的:连接器(connector)和容器(container)。在 Figure I.1 中的架构图,当然是简化 了。在稍后的章节里边,你将会一个个的揭开所有更小的组件的神秘面纱。 现在重新回到 Figure I.1,连接器是用来“连接”容器里边的请求的。它的工作是为接收 到每一个 HTTP 请求构造一个 request 和 response 对象。然后它把流程传递给容器。容器从连接 器接收到 requset 和 response 对象之后调用 servlet 的 service 方法用于响应。谨记,这个描 述仅仅是冰山一角而已。这里容器做了相当多事情。例如,在它调用 servlet 的 service 方法之 前,它必须加载这个 servlet,验证用户(假如需要的话),更新用户会话等等。一个容器为了处 理这个进程使用了很多不同的模块,这也并不奇怪。例如,管理模块是用来处理用户会话,而加 载器是用来加载 servlet 类等等。 Tomcat 4 和 5 这本书涵盖了 Tomcat4 和 5.这两者有一些不同之处: Tomcat 5 支持 Servlet 2.4 和 JSP 2.0 规范,而 Tomcat 4 支持 Servlet 2.3 和 JSP 1.2。   比起 Tomcat 4,Tomcat 5 有一些更有效率的默认连接器。   Tomcat 5 共享一个后台处理线程,而 Tomcat 4 的组件都有属于自己的后台处理线程。 因此,就这一点而言,Tomcat 5 消耗较少的资源。 Tomcat 5 并不需要一个映射组件(mapper component)用于查找子组件,因此简化了代码。 各章概述 这本书共 20 章,其中前面两章作为导言。 第 1 章说明一个 HTTP 服务器是如何工作的,第 2 章突出介绍了一个简单的 servlet 容器。 接下来的两章关注连接器,第 5 章到第 20 章涵盖容器里边的每一个组件。以下是各章节的摘要。 注意:对于每个章节,会有一个附带程序,类似于正在被解释的组件。 第 1 章从这本书一开始就介绍了一个简单的 HTTP 服务器。要建立一个可工作的 HTTP 服务器, 你需要知道在 java.net 包里边的 2 个类的内部运作:Socket 和 ServerSocket。这里有关于这 2 个类足够的背景资料,使得你能够理解附带程序是如何工作的。 第 2 章说明简单的 servlet 容器是如何工作的。这一章带有 2 个 servlet 容器应用,可以处 理静态资源和简单的 servlet 请求。尤其是你将会学到如何创建 request 和 response 对象,然
后把它们传递给被请求的 servlet 的 service 方法。在 servlet 容器里边还有一个 servlet,你 可以从一个 web 浏览器中调用它。 第 3 章介绍了一个简化版本的 Tomcat 4 默认连接器。这章里边的程序提供了一个学习工具, 用于理解第 4 章里边的讨论的连接器。 第 4 章介绍了 Tomcat 4 的默认连接器。这个连接器已经不推荐使用,推荐使用一个更快的 连接器,Coyote。不过,默认的连接器更简单,更易于理解。 第 5 章讨论 container 模块。container 指的是 org.apache.catalina.Container 接口,有 4 种类型的 container:engine, host, context 和 wrapper。这章提供了两个工作于 context 和 wrapper 的程序。 第 6 章解释了 Lifecycle 接口。这个接口定义了一个 Catalina 组件的生命周期,并提供了 一个优雅的方式,用来把在该组件发生的事件通知其他组件。另外,Lifecycle 接口提供了一个 优雅的机制,用于在 Catalina 通过单一的 start/stop 来启动和停止组件 第 7 章包括日志,该组件是用来记录错误信息和其他信息的。 第 8 章解释了加载器(loader)。加载器是一个重要的 Catalina 模块,负责加载 servlet 和 一个 web 应用所需的其他类。这章还展示了如何实现应用的重新加载。 第 9 章讨论了管理器(manager)。这个组件用来管理会话管理中的会话信息。它解释了各式 各样类型的管理器,管理器是如何把会话对象持久化的。在章末,你将会学到如何创建一个的应 用,该应用使用 StandardManager 实例来运行一个使用会话对象进行储值的 servlet。 第 10 章包括 web 应用程序安全性的限制,用来限制进入某些内容。你将会学习与安全相关 的实体,例如 主角(principals),角色(roles),登陆配置,认证等等。你也将会写两个程序,它们在 StandardContext 对象中安装一个身份验证阀(authenticator valve)并且使用了基本的认证来 对用户进行认证。 第 11 章 详 细 解 释 了 在 一 个 web 应 用 中 代 表 一 个 servlet 的 org.apache.catalina.core.StandardWrapper 类。特别的是,这章解释了过滤器(filter)和一 个 servlet 的 service 方法是怎样给调用的。这章的附带程序使用 StandardWrapper 实例来代表 servlet。 第 12 章 包 括 了 在 一 个 web 应 用 中 代 表 一 个 servlet 的 org.apache.catalina.core.StandardContext 类。特别是这章讨论了一个 StandardContext 对 象是如何给配置的,对于每个传入的 HTTP 请求在它里面会发生什么,是怎样支持自动重新加载 的,还有就是,在一个在其相关的组件中执行定期任务的线程中,Tomcat 5 是如何共享的。 第 13 章介绍了另外两个容器:host 和 engine。你也同样可以找到这两个容器的标准实 现:org.apache.catalina.core.StandardHost 和 org.apache.catalina.core.StandardEngine。 第 14 章提供了服务器和服务组件的部分。服务器为整个 servlet 容器提供了一个优雅的启 动和停止机制,而服务为容器和一个或多个连接器提供了一个支架。这章附带的程序说明了如何 使用服务器和服务。 第 15 章解释了通过 Digester 来配置 web 应用。Digester 是来源于 Apache 软件基金会的一 个令人振奋的开源项目。对那些尚未初步了解的人,这章通过一节略微介绍了 Digester 库以及 XML 文件中如何使用它来把节点转换为 Java 对象。然后解释了用来配置一个 StandardContext 实例的 ContextConfig 对象。 第 16 章解释了 shutdown 钩子,Tomcat 使用它总能获得一个机会用于 clean-up,而无论用 户是怎样停止它的(即适当的发送一个 shutdown 命令或者不适当的简单关闭控制台)。 第 17 章讨论了通过批处理文件和 shell 脚本对 Tomcat 进行启动和停止。 第 18 章介绍了部署工具(deployer),这个组件是负责部署和安装 web 应用的。
第 19 章讨论了一个特殊的接口,ContainerServlet,能够让 servlet 访问 Catalina 的内部 对象。特别是,它讨论了 Manager 应用,你可以通过它来部署应用程序。 第 20 章讨论了 JMX 以及 Tomcat 是如何通过为其内部对象创建 MBeans 使得这些对象可管理 的。 各章的程序 每一章附带了一个或者多个程序,侧重于 Catalina 的一个特定的组件。通常你可以找到这 些简化版本,无论是正在被解释的组件或者解释如何使用 Catalina 组件的代码。各章节的程序 的所有的类和接口都放在 ex[章节号].pyrmont 包或者它的子包。例如第 1 章的程序的类就是放 在 ex01.pyrmont 包中。 准备的前提软件 这 本 书 附 带 的 程 序 运 行 于 J2SE1.4 版 本 。 压 缩 源 文 件 可 以 从 作 者 的 网 站 www.brainysoftware.com 中下载。它包括 Tomcat 4.1.12 和这本书所使用的程序的源代码。假 设你已经安装了 J2SE 1.4 并且你的 path 环境变量中已经包括了 JDK 的安装目录,请按照下列步 骤: 1. 解压缩 ZIP 文件。所有的解压缩文件将放在一个新的目录 howtomcatworks 中。 howtomcatworks 将是你的工作目录。在 howtomcatworks 目录下面将会有数个子目录, 包括 lib (包括所有所需的库),src (包括所有的源文件),webroot (包括一个 HTML 文 件和三个 servlet 样本),和 webapps (包括示例应用程序)。 2. 改变目录到工作目录下并编译 java 文件。加入你使用的是 Windows,运行 win-compile.bat 文件。假如你的计算机是 Linux 机器,敲入以下内容:(如有必要的话 不用忘记使用 chmod 更改文件属性) ./linux-compile.sh 注意:你可以在 ZIP 文件中的 Readme.txt 文件找到更多信息。 第一章:一个简单的 Web 服务器 本章说明 java web 服务器是如何工作的。Web 服务器也成为超文本传输协议(HTTP)服务器,
因为它使用 HTTP 来跟客户端进行通信的,这通常是个 web 浏览器。一个基于 java 的 web 服务器 使用两个重要的类:java.net.Socket 和 java.net.ServerSocket,并通过 HTTP 消息进行通信。 因此这章就自然是从 HTTP 和这两个类的讨论开始的。接下去,解释这章附带的一个简单的 web 服务器。 超文本传输协议(HTTP) HTTP 是一种协议,允许 web 服务器和浏览器通过互联网进行来发送和接受数据。它是一种 请求和响应协议。客户端请求一个文件而服务器响应请求。HTTP 使用可靠的 TCP 连接--TCP 默认 使用 80 端口。第一个 HTTP 版是 HTTP/0.9,然后被 HTTP/1.0 所替代。正在取代 HTTP/1.0 的是 当 前 版 本 HTTP/1.1 , 它 定 义 于 征 求 意 见 文 档 (RFC) 2616 , 可 以 从 http://www.w3.org/Protocols/HTTP/1.1/rfc2616.pdf 下载。 注意:本节涵盖的 HTTP 1.1 只是简略的帮助你理解 web 服务器应用发送的消息。假如你对 更多详细信息感兴趣,请阅读 RFC 2616。 在 HTTP 中,始终都是客户端通过建立连接和发送一个 HTTP 请求从而开启一个事务。web 服 务器不需要联系客户端或者对客户端做一个回调连接。无论是客户端或者服务器都可以提前终止 连接。举例来说,当你正在使用一个 web 浏览器的时候,可以通过点击浏览器上的停止按钮来停 止一个文件的下载进程,从而有效的关闭与 web 服务器的 HTTP 连接。 HTTP 请求 一个 HTTP 请求包括三个组成部分:  方法—统一资源标识符(URI)—协议/版本  请求的头部  主体内容 下面是一个 HTTP 请求的例子: POST /examples/default.jsp HTTP/1.1 Accept: text/plain; text/html Accept-Language: en-gb Connection: Keep-Alive Host: localhost User-Agent: Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) Content-Length: 33 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate lastName=Franks&firstName=Michael 方法—统一资源标识符(URI)—协议/版本出现在请求的第一行。 POST /examples/default.jsp HTTP/1.1 这里 POST 是请求方法,/examples/default.jsp 是 URI,而 HTTP/1.1 是协议/版本部分。 每个 HTTP 请求可以使用 HTTP 标准里边提到的多种方法之一。HTTP 1.1 支持 7 种类型的请 求:GET, POST, HEAD, OPTIONS, PUT, DELETE 和 TRACE。GET 和 POST 在互联网应用里边最普遍使用的。 URI 完全指明了一个互联网资源。URI 通常是相对服务器的根目录解释的。因此,始终一斜 线/开头。统一资源定位器(URL)其实是一种 URI(查看 http://www.ietf.org/rfc/rfc2396.txt)
来的。该协议版本代表了正在使用的 HTTP 协议的版本。 请求的头部包含了关于客户端环境和请求的主体内容的有用信息。例如它可能包括浏览器设 置的语言,主体内容的长度等等。每个头部通过一个回车换行符(CRLF)来分隔的。 对于 HTTP 请求格式来说,头部和主体内容之间有一个回车换行符(CRLF)是相当重要的。CRLF 告诉 HTTP 服务器主体内容是在什么地方开始的。在一些互联网编程书籍中,CRLF 还被认为是 HTTP 请求的第四部分。 在前面一个 HTTP 请求中,主体内容只不过是下面一行: lastName=Franks&firstName=Michael 实体内容在一个典型的 HTTP 请求中可以很容易的变得更长。 HTTP 响应 类似于 HTTP 请求,一个 HTTP 响应也包括三个组成部分:  方法—统一资源标识符(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 HTTP Response Example Welcome to Brainy Software 响应头部的第一行类似于请求头部的第一行。第一行告诉你该协议使用 HTTP 1.1,请求成 功(200=成功),表示一切都运行良好。 响应头部和请求头部类似,也包括很多有用的信息。响应的主体内容是响应本身的 HTML 内 容。头部和主体内容通过 CRLF 分隔开来。 Socket 类 套接字是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。放在两 个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另 一个应用,你需要知道另一个应用的 IP 地址和套接字端口。在 Java 里边,套接字指的是
java.net.Socket 类。 要创建一个套接字,你可以使用 Socket 类众多构造方法中的一个。其中一个接收主机名称 和端口号: public Socket (java.lang.String host, int port) 在这里主机是指远程机器名称或者 IP 地址,端口是指远程应用的端口号。例如,要连接 yahoo.com 的 80 端口,你需要构造以下的 Socket 对象: new Socket ("yahoo.com", 80); 一旦你成功创建了一个 Socket 类的实例,你可以使用它来发送和接受字节流。要发送字节 流,你首先必须调用 Socket 类的 getOutputStream 方法来获取一个 java.io.OutputStream 对象。 要 发 送 文 本 到 一 个 远 程 应 用 , 你 经 常 要 从 返 回 的 OutputStream 对 象 中 构 造 一 个 java.io.PrintWriter 对 象 。 要 从 连 接 的 另 一 端 接 受 字 节 流 , 你 可 以 调 用 Socket 类 的 getInputStream 方法用来返回一个 java.io.InputStream 对象。 以下的代码片段创建了一个套接字,可以和本地 HTTP 服务器(127.0.0.1 是指本地主机)进 行通讯,发送一个 HTTP 请求,并从服务器接受响应。它创建了一个 StringBuffer 对象来保存响 应并在控制台上打印出来。 Socket socket = new Socket("127.0.0.1", "8080"); OutputStream os = socket.getOutputStream(); boolean autoflush = true; PrintWriter out = new PrintWriter( socket.getOutputStream(), autoflush); BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputstream() )); // send an HTTP request to the web server out.println("GET /index.jsp HTTP/1.1"); out.println("Host: localhost:8080"); out.println("Connection: Close"); out.println(); // read the response boolean loop = true; StringBuffer sb = new StringBuffer(8096); while (loop) { if ( in.ready() ) { int i=0; while (i!=-1) { i = in.read(); sb.append((char) i); } loop = false; } Thread.currentThread().sleep(50); }
// display the response to the out console System.out.println(sb.toString()); socket.close(); 请注意,为了从 web 服务器获取适当的响应,你需要发送一个遵守 HTTP 协议的 HTTP 请求。 假如你已经阅读了前面一节超文本传输协议(HTTP),你应该能够理解上面代码提到的 HTTP 请求。 注意:你可以本书附带的 com.brainysoftware.pyrmont.util.HttpSniffer 类来发送一个 HTTP 请求并显示响应。要使用这个 Java 程序,你必须连接到互联网上。虽然它有可能并不会起 作用,假如你有设置防火墙的话。 ServerSocket 类 Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候你构 造的套接字,现在,假如你想实施一个服务器应用,例如一个 HTTP 服务器或者 FTP 服务器,你 需要一种不同的做法。这是因为你的服务器必须随时待命,因为它不知道一个客户端应用什么时 候会尝试去连接它。为了让你的应用能随时待命,你需要使用 java.net.ServerSocket 类。这是 服务器套接字的实现。 ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服 务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。 要创建一个服务器套接字,你需要使用 ServerSocket 类提供的四个构造方法中的一个。你 需要指定 IP 地址和服务器套接字将要进行监听的端口号。通常,IP 地址将会是 127.0.0.1,也 就是说,服务器套接字将会监听本地机器。服务器套接字正在监听的 IP 地址被称为是绑定地址。 服务器套接字的另一个重要的属性是 backlog,这是服务器套接字开始拒绝传入的请求之前,传 入的连接请求的最大队列长度。 其中一个 ServerSocket 类的构造方法如下所示: public ServerSocket(int port, int backLog, InetAddress bindingAddress); 对于这个构造方法,绑定地址必须是 java.net.InetAddress 的一个实例。一种构造 InetAddress 对象的简单的方法是调用它的静态方法 getByName,传入一个包含主机名称的字符 串,就像下面的代码一样。 InetAddress.getByName("127.0.0.1"); 下面一行代码构造了一个监听的本地机器 8080 端口的 ServerSocket,它的 backlog 为 1。 new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1")); 一旦你有一个 ServerSocket 实例,你可以让它在绑定地址和服务器套接字正在监听的端口 上等待传入的连接请求。你可以通过调用 ServerSocket 类的 accept 方法做到这点。这个方法只 会在有连接请求时才会返回,并且返回值是一个 Socket 类的实例。Socket 对象接下去可以发送 字节流并从客户端应用中接受字节流,就像前一节"Socket 类"解释的那样。实际上,这章附带 的程序中,accept 方法是唯一用到的方法。 应用程序 我们的 web 服务器应用程序放在 ex01.pyrmont 包里边,由三个类组成:
分享到:
收藏