《深入剖析Tomcat 》第3章 连接器(Connector)

2024-06-05 20:48

本文主要是介绍《深入剖析Tomcat 》第3章 连接器(Connector),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第3章  连接器(Connector)

3.1  概述

在简介一章里说明了,tomcat由两大模块组成:连接器(connector)和容器(container)。本章将使用连接器来增强application 2的功能。一个支持servlet2.3和2.4规范的连接器必须要负责创建javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse实例,并将它们作为参数传递给要调用的某个的servlet的service方法。在第2章中的servlet容器仅仅能运行实现了javax.servlet.Servlet接口,并想service方法中传入了javax.servlet.ServletRequest和javax.servlet.ServletResponse实例的servlet。由于连接器并不知道servlet的具体类型(例如,该servlet是否javax.servlet.Servlet接口,还是继承自javax.servlet.GenericServlet类,或继承自javax.servlet.http.HttpServlet类),因此连接器总是传入HttpServletRequest和HttpServletResponse的实例对象。

         本章中所要建立的connector实际上是tomcat4中的默认连接器(将在第4章讨论)的简化版。本章中,connector和container将分离开。

         在开始说明本章的程序之前,先花点时间介绍下org.apache.catalina.util.StringManager类,它被tomcat用来处理不同模块内错误信息的国际化。在本章中,也是这样用的。

3.2  StringManager类

tomcat将错误信息写在一个properties文件中,这样便于读取和编辑。但若是将所有类的错误信息都写在一个properties文件,优惠导致文件太大,不便于读写。为避免这种情况,tomcat将properties文件按照不同的包进行划分,每个包下都有自己的properties文件。例如,org.apache.catalina.connector包下的properties文件包含了该包下所有的类中可能抛出的错误信息。每个properties文件都由一个org.apache.catalina.util.StringManager实例来处理。在tomcat运行时,会建立很多StringManager类的实例,每个实例对应一个properties文件。

         当包内的某个类要查找错误信息时,会先获取对应的StringManager实例。StringManager被设计为在包内是共享的一个单例,功过hashtable实现。如下面的代码所示:

 

java代码:
查看 复制到剪贴板 打印
  1. private static Hashtable managers = new Hashtable();   
  2. public synchronized static StringManager   
  3.     getManager(String packageName) {   
  4.    StringManager mgr = (StringManager)managers.get(packageName);    if (mgr == null) {   
  5.      mgr = new StringManager(packageName);   
  6.      managers.put(packageName, mgr);   
  7.    }   
  8.    return mgr;   
  9. }  
  10. StringManager sm = StringManager.getManager("ex03.pyrmont.connector.http");  

3.3  Application

从本章开始,每章的应用程序都会按照模块进行划分。本章的应用程序可分为3个模块:connector、startup、core。

         startup模块仅包括一个StartUp类,负责启动应用程序。

         connector模块的类可分为以下5个部分:

l         连接器及其支持类(HttpConnector和HttpProcessor);

l         表示http请求的类(HttpRequest)及其支持类;

l         表示http响应的类(HttpResponse)及其支持类;

l         外观装饰类(HttpRequestFacade和HttpResponseFacade);

l         常量类。

core模块包括ServletProcessor类和StaticResourceProcessor类。

下面是程序的uml图:

图表 4  application的uml图

相比于第2章中的程序,HttpServer在本章中被分成了HttpConnector和HttpProcessor两个类。Request和Response分别被HttpRequest和HttpResponse代替。此外,本章的应用程序中还使用了一些其他的类。

         在第2章中,HttpServer负责等待http请求,并创建request和response对象。本章中,等待http请求的工作由HttpConnector完成,创建request和response对象的工作由HttpProcessor完成。

本章中,http请求用HttpRequest对象表示,该类实现了javax.servlet.http.HttpServletRequest接口。一个HttpRequest对象在传给servlet的service方法前,会被转型为HttpServletRequest对象。因此,需要正确设置每个HttpRequest对象的成员变量,方便servlet使用。需要设置的值包括,uri,请求字符串,参数,cookie和其他一些请求头信息等。由于连接器并不知道servlet中会使用那些变量,素以它会将从http请求中获取的变量都设置到HttpRequest对象中。但是,处理一个http请求会设计到一些比较耗时的操作,如字符串处理等。因此,若是connector仅仅传入servlet需要用到的值就会节省很多时间。tomcat的默认connector对这些值的处理是等到servlet真正用到的时候才处理的。

tomcat的默认connector和本程序的connector通过SocketInputStream类来读取字节流,可通过socket的getInputStream方法来获取该对象。它有两个重要的方法readRequestLine和readHeader。readRequestLine方法返回一个http请求的第一行,包括uri,请求方法和http协议版本。从socket的inputStream中处理字节流意味着要从头读到尾(即不能返回来再读前面的内容),因此,readRequestLine方法一定要在readHeader方法前调用。readRequestLine方法返回的是HttpRequestLine对象,readHeader方法返回的是HttpHeader对象(key-value形式)。获取HttpHeader对象时,应重复调用readHeader方法,直到再也无法获取到。

HttpProcessor对象负责创建HttpRequest对象,并填充它的成员变量。在其parse方法中,将请求行(request line)和请求头(request header)信息填充到HttpRequest对象中,但并不会填充请求体(request body)和查询字符串(query string)。

3.3.1  启动

在Bootstrap类的main方法内实例化一个HttpConnector类的对象,并调用其start方法就可以启动应用程序。

3.3.2  connector

HttpConnector类实现了java.lang.Runnable接口,这样它可以专注于自己的线程。启动应用程序时,会创建一个HttpConnector对象,其run方法会被调用。其run方法中是一个循环体,执行以下三件事:

l         等待http请求;

l         为每个请求创建一个HttpPorcessor对象;

l         调用HttpProcessor对象的process方法。

         HttpProcessor类的process方法从http请求中获取socket。对每个http请求,它要做一下三件事:

l         创建一个HttpRequest对象和一个HttpResponse对象;

l         处理请求行(request line)和请求头(request headers),填充HttpRequest对象;

l         将HttpRequest对象和HttpResponse对象传给ServletProcessor或StaticResourceProcessor的process方法。

 

3.3.3  创建HttpRequest对象

HttpRequest类实现了javax.servlet.http.HttpServletRequest接口。其伴随的外观类是HttpRequestFacade。日uml图如下所示:


图表 5  HttpRequest类的uml图

其中HttpRequest的很多方法都是空方法,但已经可以从hhtp请求中获取headers,cookies和参数信息了。这三种数据分别以HashMap、ArrayList和ParameterMap(后面介绍)存储。

3.3.3.1  SocketInputStream类

本章的应用程序中,使用的SocketInputStream就是org.apache.catalina.connector.http.SocketInputStream。该类提供了获取请求行(request line)和请求头(request header)的方法。通过传入一个InputStream对象和一个代表缓冲区大小的整数值来创建SocketInputStream对象。

3.3.3.2  解析请求行(request line)

HttpProcessor的process调用其私有方法parseRequest来解析请求行(request line,即http请求的第一行)。下面是一个请求行(request line)的例子:

         GET /myApp/ModernServlet?userName=tarzan&password=pwd HTTP/1.1

         注意:“GET”后面和“HTTP”前面各有一个空格。

         请求行的第2部分是uri加上查询字符串。在上面的例子中,uri是:

/myApp/ModernServlet

问号后面的都是查询字符串,这里是:

userName=tarzan&password=pwd

在servlet/jsp编程中,参数jsessionid通常是嵌入到cookie中的,也可以将其嵌入到查询字符串中。parseRequest方法的具体内容参见代码。

         

3.3.3.3  解析请求头(request header)

请求头(request header)由HttpHeader对象表示。可以通过HttpHeader的无参构造方法建立对象,并将其作为参数传给SocketInputStream的readHeader方法,该方法会自动填充HttpHeader对象。parseHeader方法内有一个循环体,不断的从SocketInputStream中读取header信息,直到读完。获取header的name和value值可使用下米娜的语句:

 

java代码:
查看 复制到剪贴板 打印
  1. String name = new String(header.name, 0, header.nameEnd);   
  2. String value = new String(header.value, 0, header.valueEnd);  
  3. 获取到header的name和value后,要将其填充到HttpRequest的header属性(hashMap类型)中:  
  4. request.addHeader(name, value);  
  5. 其中某些header要设置到request对象的属性中,如contentLength等。  
3.3.3.4  解析cookie

ookie是由浏览器作为请求头的一部分发送的,这样的请求头的名字是cookie,它的值是一个key-value对。举例如下:

 

java代码:
查看 复制到剪贴板 打印
  1. Cookie: userName=budi; password=pwd;  

对cookie的解析是通过org.apache.catalina.util.RequestUtil类的parseCookieHeader方法完成的。该方法接受一个cookie头字符串,返回一个javax.servlet.http.Cookie类型的数组。方法实现如下:

 

java代码:
查看 复制到剪贴板 打印
  1. public static Cookie[] parseCookieHeader(String header) {   
  2. if ((header == null) || (header.length 0 < 1) )   
  3.     return (new Cookie[0]);   
  4.         ArrayList cookies = new ArrayList();   
  5. while (header.length() > 0) {   
  6.             int semicolon = header.indexOf(';');   
  7.             if (semicolon < 0)   
  8.     semicolon = header.length();   
  9.     if (semicolon == 0)   
  10.     break;   
  11.     String token = header.substring(0, semicolon);   
  12.     if (semicolon < header.length())   
  13.        header = header.substring(semicolon + 1);   
  14.     else   
  15.                 header = "";   
  16.     try {   
  17.        int equals = token.indexOf('=');   
  18.                if (equals > 0) {   
  19.        String name = token.substring(0, equals).trim();   
  20.                    String value = token.substring(equals+1).trim();   
  21.        cookies.add(new Cookie(name, value));   
  22.                 }   
  23. catch (Throwable e) { ; }   
  24. }   
  25. return ((Cookie[]) cookies.toArray (new Cookie [cookies.size ()]));   
  26. }  
3.3.3.5  获取参数

在调用javax.servlet.http.HttpServletRequest的getParameter、getParameterMap、getParameterNames或getParameterValues方法之前,都不会涉及到对查询字符串或http请求体的解析。因此,这四个方法的实现都是先调用parseParameter方法。

         参数只会被解析一次,因为,HttpRequest类会设置一个标志位表明是否已经完成参数解析了。参数可以出现在查询字符串或请求体中。若用户使用的GET方法,则所有的参数都会在查询字符串中;若是使用的POST方法,则请求体中也可能会有参数。所有的key-value的参数对都会存储在HashMap中,其中的值是不可修改的。tomcat中使用的是一个特殊的hashmap类,org.apache.catalina.util.ParameterMap。

         ParameterMap类继承自java.util.HashMap,使用一个标志位来表示锁定。如果该标志位为false,则可以对其中的key-value进行添加、修改、删除操作,否则,执行这些操作时,会抛出IllegalStateException异常。代码如下:

3.3.3.6  创建HttpResponse对象

HttpResponse类继承自javax.servlet.http.HttpServletResponse,其相应的外观类是HttpResponseFacade。其uml图如下所示:

图表 6  HttpResponse及其外观类的uml图示

在第2章中,HttpResponse的功能有限,例如,它的getWriter方法返回的java.io.PrintWriter对象执行了print方法时,并不会自动flush。本章的程序将解决此问题。在此之前,先说明一下什么是Writer。

         在servlet中,可以使用PrintWriter对象想输出流中写字符。可以使用任意编码格式,但在发送的时候,实际上都是字节流。

         在本章中,将要使用的是ex03.pyrmont.connector.ResponseStream类作为PrintWriter的输出流。该类直接继承自java.io.OutputStream类。

         类ex03.pyrmont.connector.ResponseWriter继承自PrintWriter,重写了其print和println方法,实现自动flush。因此,本章适用ResponseWriter作为输出对象。

         示例代码如下:

 

java代码:
查看 复制到剪贴板 打印
  1. public PrintWriter getWriter() throws IOException {   
  2.      ResponseStream newStream = new ResponseStream(this);   
  3.      newStream.setCommit(false);   
  4.      OutputStreamWriter osr =   
  5.        new OutputStreamWriter(newStream, getCharacterEncoding());   
  6.      writer = new ResponseWriter(osr);   
  7.      return writer;   
  8.    }  
3.3.3.7  静态资源处理器和servlet处理器

         本章的servlet处理器和第2章的servlet处理器类似,都只有一个process方法。但个,本章中,process方法接收的参数类型为HttpRequest和HttpResponse。方法签名如下:

         public void process(HttpRequest request, HttpResponse response);

         此外,process使用了request和response的外观类,并在调用了servlet的service方法后,再调用HttpResponse的finishResponse方法。示例代码如下:

 

java代码:
查看 复制到剪贴板 打印
  1. servlet = (Servlet) myClass.newInstance();   
  2.       HttpRequestFacade requestPacade = new HttpRequestFacade(request);   
  3.       HttpResponseFacade responseFacade = new   
  4.         HttpResponseFacade(response);   
  5.       servlet.service(requestFacade, responseFacade);   
  6.       ((HttpResponse) response).finishResponse();  

 

转载请注明出处【http://sishuok.com/forum/blogPost/list/0/4076.html】

这篇关于《深入剖析Tomcat 》第3章 连接器(Connector)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1034142

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入手撕链表

链表 分类概念单链表增尾插头插插入 删尾删头删删除 查完整实现带头不带头 双向链表初始化增尾插头插插入 删查完整代码 数组 分类 #mermaid-svg-qKD178fTiiaYeKjl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

详解Tomcat 7的七大新特性和新增功能(1)

http://developer.51cto.com/art/201009/228537.htm http://tomcat.apache.org/tomcat-7.0-doc/index.html  Apache发布首个Tomcat 7版本已经发布了有一段时间了,Tomcat 7引入了许多新功能,并对现有功能进行了增强。很多文章列出了Tomcat 7的新功能,但大多数并没有详细解释它们

Tomcat性能参数设置

转自:http://blog.csdn.net/chinadeng/article/details/6591542 Tomcat性能参数设置 2010 - 12 - 27 Tomcat性能参数设置 博客分类: Java Linux Tomcat 网络应用 多线程 Socket 默认参数不适合生产环境使用,因此需要修改一些参数   1、修改启动时内存参数、并指定J

TL-Tomcat中长连接的底层源码原理实现

长连接:浏览器告诉tomcat不要将请求关掉。  如果不是长连接,tomcat响应后会告诉浏览器把这个连接关掉。    tomcat中有一个缓冲区  如果发送大批量数据后 又不处理  那么会堆积缓冲区 后面的请求会越来越慢。

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品