• Tomcat源码学习(13)How Tomcat works(转)


    第3章:连接器

    概要

        在介绍中提到,Catalina中有两个主要的模块:连接器和容器。本章中你将会写一个可以创建更好的请求和响应对象的连接器,用来改进第2章中的程序。一个符合Servlet 2.3和2.4规范的连接器必须创建javax.servlet.http.HttpServletRequest和javax.servlet.http.HttpServletResponse,并传递给被调用的servlet的service方法。在第2章 中,servlet容器只可以运行实现了javax.servlet.Servlet的servlet,并传递 javax.servlet.ServletRequest和javax.servlet.ServletResponse实例给service方法。因为连接器并不知道servlet的类型(例如它是否实现了javax.servlet.Servlet,继承了javax.servlet.GenericServlet,或者继承了javax.servlet.http.HttpServlet),所以连接器必须始终提供HttpServletRequest和HttpServletResponse的实例。
        在本章的应用程序中,连接器解析HTTP请求头部并让servlet可以获得头部, cookies, 参数名/值等等。你将会完善第2章中Response类的getWriter方法,让它能够正确运行。由于这些改进,你将会从 PrimitiveServlet中获取一个完整的响应,并能够运行更加复杂的ModernServlet。
        本章你建立的连接器是将在第4章详细讨论的Tomcat4的默认连接器的一个简化版本。Tomcat的默认连接器在Tomcat4中是不推荐使用的,但它仍然可以作为一个非常棒的学习工具。在这章的剩余部分,"connector"指的是内置在我们应用程序的模块。
        注意:和上一章的应用程序不同的是,本章的应用程序中,连接器和容器是分离的。
        本章的应用程序可以在包ex03.pyrmont和它的子包中找到。组成连接器的这些类是包
    ex03.pyrmont.connector 和ex03.pyrmont.connector.http的一部分。在本章的开头,每个附带的程序都有个bootstrap类用来启动应用程序。不过,在这个阶段,尚未有一个机制来停止这个应用程序。一旦运行,你必须通过关闭控制台(Windows)或者杀死进程(UNIX/Linux)的方法来鲁 莽的关闭应用程序。
        在我们解释该应用程序之前,让我们先来说说包org.apache.catalina.util里边的StringManager类。这个类用来处理这个程序中不同模块和Catalina自身的错误信息的国际化。之后会讨论附带的应用程序。

    StringManager类

        一个像Tomcat这样的大型应用需要仔细的处理错误信息。在Tomcat中,错误信息对于系统管理员和servlet程序员都是有用的。例 如,Tomcat记录错误信息,让系统管理员可以定位发生的任何异常。对servlet程序员来说,Tomcat会在抛出的任何一个 javax.servlet.ServletException中发送一个错误信息,这样程序员可以知道他/她的servlet究竟发送什么错误了。
        Tomcat所采用的方法是在一个属性文件里边存储错误信息,这样,可以容易的修改这些信息。不过,Tomcat中有数以百计的类。把所有类使用的错误信 息存储到一个大的属性文件里边将会容易产生维护的噩梦。为了避免这一情况,Tomcat为每个包都分配一个属性文件。例如,在包 org.apache.catalina.connector里边的属性文件包含了该包所有的类抛出的所有错误信息。每个属性文件都会被一个 org.apache.catalina.util.StringManager类的实例所处理。当Tomcat运行时,将会有许多 StringManager实例,每个实例会读取包对应的一个属性文件。此外,由于Tomcat的受欢迎程度,提供多种语言的错误信息也是有意义的。目前,有三种语言是被支持的。英语的错误信息属性文件名为LocalStrings.properties。另外两个是西班牙语和日语,分别放在 LocalStrings_es.properties和LocalStrings_ja.properties里边。
        当包里边的一个类需要查找放在该包属性文件的一个错误信息时,它首先会获得一个StringManager实例。不过,相同包里边的许多类可能也需要 StringManager,为每个对象创建一个StringManager实例是一种资源浪费。因此,StringManager类被设计成一个StringManager实例可以被包里边的所有类共享。假如你熟悉设计模式,你将会正确的猜到StringManager是一个单例 (singleton)类。仅有的一个构造方法是私有的,所有你不能在类的外部使用new关键字来实例化。你通过传递一个包名来调用它的公共静态方法 getManager来获得一个实例。每个实例存储在一个以包名为键(key)的Hashtable中。
    private static Hashtable managers = new Hashtable();
    public synchronized static StringManager
    getManager(String packageName) {
         StringManager mgr = (StringManager)managers.get(packageName);
         if (mgr == null) {
             mgr = new StringManager(packageName);
             managers.put(packageName, mgr);
         }
         return mgr;
    }
        注意:一篇关于单例模式的题为"The Singleton Pattern"的文章可以在附带的ZIP文件中找到。
        例如,要在包ex03.pyrmont.connector.http的一个类中使用StringManager,可以传递包名给StringManager类的getManager方法:
    StringManager sm =
         StringManager.getManager("ex03.pyrmont.connector.http");
        在包ex03.pyrmont.connector.http中,你会找到三个属性文件:LocalStrings.properties, LocalStrings_es.properties和LocalStrings_ja.properties。StringManager实例是根据运行程序的服务器的区域设置来决定使用哪个文件的。假如你打开LocalStrings.properties,非注释的第一行是这样的:
    httpConnector.alreadyInitialized=HTTP connector has already been initialized
        要获得一个错误信息,可以使用StringManager类的getString,并传递一个错误代号。这是其中一个重载方法:
    public String getString(String key)
        通过传递httpConnector.alreadyInitialized作为getString的参数,将会返回"HTTP connector has already been initialized"。

    应用程序

        从本章开始,每章附带的应用程序都会分成模块。这章的应用程序由三个模块组成:connector,
    startup和core。
        startup模块只有一个类,Bootstrap,用来启动应用的。connector模块的类可以分为五组:
    • 连接器和它的支撑类(HttpConnector和HttpProcessor)。
    • 指代HTTP请求的类(HttpRequest)和它的辅助类。
    • 指代HTTP响应的类(HttpResponse)和它的辅助类。
    • Facade类(HttpRequestFacade和HttpResponseFacade)。
    • Constant类
        core模块由两个类组成:ServletProcessor和StaticResourceProcessor。
        Figure 3.1显示了这个应用的类的UML图。为了让图更具可读性,HttpRequest和HttpResponse相关的类给省略了。你可以在我们讨论Request和Response对象的时候分别找到UML图。
                 Figure 3.1: 应用程序的UML图
        和Figure 2.1的UML图相比,第2章中的HttpServer类被分离为两个类:HttpConnector和HttpProcessor,Request被 HttpRequest所取代,而Response被HttpResponse所取代。同样,本章的应用使用了更多的类。
        第2章中的HttpServer类的职责是等待HTTP请求并创建请求和响应对象。在本章的应用中,等待HTTP请求的工作交给HttpConnector实例,而创建请求和响应对象的工作交给了HttpProcessor实例。
        本章中,HTTP请求对象由实现了javax.servlet.http.HttpServletRequest的HttpRequest类来代表。一个 HttpRequest对象将会给转换为一个HttpServletRequest实例并传递给被调用的servlet的service方法。因此,每个 HttpRequest实例必须适当增加字段,以便servlet可以使用它们。值需要赋给HttpRequest对象,包括URI,查询字符串,参数,cookies和其他的头部等等。因为连接器并不知道被调用的servlet需要哪个值,所以连接器必须从HTTP请求中解析所有可获得的值。不过,解析一个HTTP请求牵涉昂贵的字符串和其他操作,假如只是解析servlet需要的值的话,连接器就能节省许多CPU周期。例如,假如servlet不 解析任何一个请求参数(例如不调用javax.servlet.http.HttpServletRequest的getParameter, getParameterMap,getParameterNames或者getParameterValues方法),连接器就不需要从查询字符串或者 HTTP请求内容中解析这些参数。Tomcat的默认连接器(和本章应用程序的连接器)试图不解析参数直到servlet真正需要它的时候,通过这样来获得更高效率。
        Tomcat的默认连接器和我们的连接器使用SocketInputStream类来从套接字的InputStream中读取字节流。一个 SocketInputStream实例对从套接字的getInputStream方法中返回的java.io.InputStream实例进行包装。 SocketInputStream类提供了两个重要的方法:readRequestLine和readHeader。readRequestLine返回一个HTTP请求的第一行。例如,这行包括了URI,方法和HTTP版本。因为从套接字的输入流中处理字节流意味着只读取一次,从第一个字节到最后一个字节(并且不回退),因此readHeader被调用之前,readRequestLine必须只被调用一次。readHeader每次被调用来获得一个头部的名/值对,并且应该被重复的调用知道所有的头部被读取到。readRequestLine的返回值是一个HttpRequestLine的实例,而 readHeader的返回值是一个HttpHeader对象。我们将在下节中讨论类HttpRequestLine和HttpHeader。
        HttpProcessor对象创建了HttpRequest的实例,因此必须在它们当中增加字段。HttpProcessor类使用它的parse方法 来解析一个HTTP请求中的请求行和头部。解析出来并把值赋给HttpProcessor对象的这些字段。不过,parse方法并不解析请求内容或者请求 字符串里边的参数。这个任务留给了HttpRequest对象它们。只是当servlet需要一个参数时,查询字符串或者请求内容才会被解析。
        另一个跟上一个应用程序比较的改进是用来启动应用程序的bootstrap类ex03.pyrmont.startup.Bootstrap的出现。
        我们将会在下面的子节里边详细说明该应用程序:
    • 启动应用程序
    • 连接器
    • 创建一个HttpRequest对象
    • 创建一个HttpResponse对象
    • 静态资源处理器和servlet处理器
    • 运行应用程序

    启动应用程序

        你可以从ex03.pyrmont.startup.Bootstrap类来启动应用程序。这个类在Listing 3.1中给出。
             Listing 3.1: Bootstrap类
    package ex03.pyrmont.startup;
    import ex03.pyrmont.connector.http.HttpConnector;
    public final class Bootstrap {
         public static void main(String[] args) {
             HttpConnector connector = new HttpConnector();
             connector.start();
         }
    }
        Bootstrap类中的main方法实例化HttpConnector类并调用它的start方法。HttpConnector类在Listing 3.2给出。
             Listing 3.2: HttpConnector类的start方法
    package ex03.pyrmont.connector.http;
    import java.io.IOException;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    public class HttpConnector implements Runnable {
         boolean stopped;
         private String scheme = "http";
         public String getScheme() {
             return scheme;
         }
         public void run() {
             ServerSocket serverSocket = null;
             int port = 8080;
             try {
                 serverSocket = new
                 ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
             }
             catch (IOException e) {
                 e.printStackTrace();
                 System.exit(1);
             }
             while (!stopped) {
                 // Accept the next incoming connection from the server socket
                 Socket socket = null;
                 try {
                     socket = serverSocket.accept();
                }
                 catch (Exception e) {
                     continue;
                 }
                 // Hand this socket off to an HttpProcessor
                 HttpProcessor processor = new HttpProcessor(this);
                 processor.process(socket);
             }
         }
         public void start() {
             Thread thread = new Thread(this);
             thread.start ();
         }
    }
  • 相关阅读:
    loadrunner数据库MySQL参数化列表乱码问题
    [MySQL]导入导出
    [MySQL]命令行工具和基本操作
    [MySQL]安装和启动
    win7下loadrunner创建mysql数据库参数化问题解决
    Win7-64bit系统下安装mysql的ODBC驱动
    loadrunner个版本历程
    性能分析与调优的原理
    性能分析与调优的原理
    loadrunner解决“服务器正在运行中”方法
  • 原文地址:https://www.cnblogs.com/macula7/p/1960784.html
Copyright © 2020-2023  润新知