How Tomcat Works学习笔记<四>

 

Tomcat4默认连接器

Tomcat的连接器(Connector)采用模块化思想设计,作为一个独立的模块与Servlet Container相连接。目前已经有多种连接器,像Coyote、mod_jk、mod_jk2、mod_webapp等,Tomcat4中的默认连接器目前已经被速度更快的Coyote代替。但是Tomcat4的默认连接器仍然是一个很好的学习工具,有助于理解Tomcat连接器的原理,所有的Tomcat连接器只需满足一下三大:

1、  实现org.apache.catalina.Connector接口;

2、  创建实现了org.apache.catalina.Request接口的request对象;

3、  创建实现了org.apache.catalina.Response接口的response对象。

 

默认连接器会调用侦听HTTP请求,创建request和response通过调用org.apache.catalina.Container接口的invoke方法把request和response传给Container:

public void invoke(org.apache.catalina.Request request, org.apache.catalina.Response response);

同时默认连接器提供了一个池用来缓存变量,并把很多String改成了char数组。

默认连接器全面支持HTTP1.1,同时兼任HTTP0.9和HTTP1.0.

 

HTTP1.1的新特性

1.       支持持久化TCP连接。在以前版本中一旦服务端收到请求相关资源,马上就被关闭,但是一个请求页面中可能会引用很多其它信息,如图片等,如果每个资源都用一个连接会比较耗时,完全可以在一个连接里面处理。HTTP1.1中增加了支持化连接,解决了上述问题。通过request header中指定connection:keep-alive实现

2.       支持块编码,分块传输。

3.       传输一个继续状态(100)。

Connector接口

         所有的Tomcat连接器都必须实现org.apache.catalina.Connector接口,其中提供了很多需要实现的方法,最重要的有getContainer、setContainer、createRequest和createResponse方法。

         setContainer用来为连接器分配一个servlet容器,getContainer获取分配给连接器的容器,createRequest为来自客户端的请求创建一个request对象,createResponse为请求创建响应(Response)。Connector的类图如下:

         其中Connector与Container的关系式一对一的,而HttpConnector与HttpProcessor对象是一对多的。

HttpConnector类

         HttpConnector是Connector的实现类,即Tomcat默认的连接器类,作为组件(Component)必须实现org.apache.catalina.Lifecycle,具有生命周期,要在Tomcat中创建一个连接器必须先调用它的init方法进行初始化,然后调用start方法启动。

创建ServerSocket

         在调用HttpConnector的启动方法的时候,会创建一个ServerSocket对象,用来侦听客户端的请求。在创建ServerSocket对象的时候使用了工厂模式,提供了一个工厂接口ServerSocketFactory与工厂实现类DefaultServerSocketFactory,我先在这里应该是为了简化ServerSocket创建的过程,便于处理多个端口的情况。

缓存HttpProcessor实例

         为了并发处理Request,提高服务器的处理效率,这里用Stack提供了对HttpProcessor进行缓冲,同时通过minProcessors和maxProcessors来控制并发数,一旦请求超过maxProcessors将会被忽略。

为HTTP Request服务

         对于侦听到的每一个HTTP请求(Socket),HttpConnector都会去从缓冲栈中获取一个处理器HttpProcessor,并把获取到的Socket分配刚刚获取到的HttpProcessor。

HttpProcessor对象

         Http 请求处理器是一个线程,不断调用await方法获取socket,一旦取到以后就调用process方法进行处理,处理完成以后会把当前处理器放入缓冲栈里面。

         run方法循环调用await获取socket进行处理,处理完成以后把处理器放回缓冲栈:

         public void run() {

        // Process requests until we receive a shutdown signal

        while (!stopped) {

            // Wait for the next socket to be assigned

            Socket socket = await();

            if (socket == null)

                continue;

            // Process the request from this socket

            try {

                process(socket);

            } catch (Throwable t) {

                log("process.invoke", t);

            }

            // Finish up this request

            connector.recycle(this);

        }

        // Tell threadStop() we have shut ourselves down successfully

        synchronized (threadSync) {

            threadSync.notifyAll();

        }

    }

         await方法获取socket,如果不能获取调用wait方法把当前线程挂起:

         private synchronized Socket await() {

        // Wait for the Connector to provide a new Socket

        while (!available) {

            try {

                wait();

            } catch (InterruptedException e) {

            }

        }

        // Notify the Connector that we have received this Socket

        Socket socket = this.socket;

        available = false;

        notifyAll();

        if ((debug >= 1) && (socket != null))

            log("  The incoming request has been awaited");

        return (socket);

    }

assign方法为处理器分配需要处理的socket,并唤醒处理器中的线程处理socket:

synchronized void assign(Socket socket) {

        // Wait for the Processor to get the previous Socket

        while (available) {

            try {

                wait();

            } catch (InterruptedException e) {

            }

        }

        // Store the newly available Socket and notify our thread

        this.socket = socket;

        available = true;

        notifyAll();

        if ((debug >= 1) && (socket != null))

            log(" An incoming request is being assigned");

    }

Request对象

         在默认连接器中Http请求用org.apache.catalina.Request接口表示具体类关系见下图:

Response对象

         Response接口及其相关类如下图:

 

处理请求

         一旦HttpProcessor组件中线程获取到socket以后,会调用process方法对request请求进行处理,主要做下面三步:

1、  解析connection

2、  解析request

3、  解析request header

简单servlet容器

         要测试连接器还需要提供两个类Bootstray和SimpleContainer,Bootstray用来启动应用,SimpleContainer是servlet容器,实现org.ahache.catalina.Container接口,其中有一个关键方法inveke回去加载servlet并调用其的service方法:

         public void invoke(Request request, Response response)

    throws IOException, ServletException {

    String servletName = ( (HttpServletRequest) request).getRequestURI();

    servletName = servletName.substring(servletName.lastIndexOf("/") + 1);

    URLClassLoader loader = null;

    try {

      URL[] urls = new URL[1];

      URLStreamHandler streamHandler = null;

      File classPath = new File(WEB_ROOT);

      String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;

      urls[0] = new URL(null, repository, streamHandler);

      loader = new URLClassLoader(urls);

    }

    catch (IOException e) {

      System.out.println(e.toString() );

    }

    Class myClass = null;

    try {

      myClass = loader.loadClass(servletName);

    }

    catch (ClassNotFoundException e) {

      System.out.println(e.toString());

    }

    Servlet servlet = null;

    try {

      servlet = (Servlet) myClass.newInstance();

      servlet.service((HttpServletRequest) request, (HttpServletResponse) response);

    }

    catch (Exception e) {

      System.out.println(e.toString());

    }

    catch (Throwable e) {

      System.out.println(e.toString());

    }

  }

Bootstray会构造连接器和容器,把容器分配给连接器,先调用连接器的init方法进行初始化,然后调用连接器的start方法启动连接器,如下:

public final class Bootstrap {

  public static void main(String[] args) {

    HttpConnector connector = new HttpConnector();

    SimpleContainer container = new SimpleContainer();

    connector.setContainer(container);

    try {

      connector.initialize();

      connector.start();

      // make the application wait until we press any key.

      System.in.read();

    }

    catch (Exception e) {

      e.printStackTrace();

    }

  }

}

运行应用

要运行应用,需要导入servlet-api.jar(servlet2.4以后)或servlet.jar(servlet2.3),同时在需要把PrimitiveServlet.java编译后放在webroot下,在页面浏览器中输入

        http://localhost:8080/servlet/PrimitiveServlet

将会在页面输出:

         Hello.Roses are red. Violets are blue.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值