how tomcat works 读书笔记四 tomcat的默认连接器

事实上在第三章,就已经有了连接器的样子了,只是那仅仅是一个学习工具,在这一章我们会開始分析tomcat4里面的默认连接器。

连接器

Tomcat连接器必须满足下面几个要求

1 实现org.apache.cataline.Connector接口
2 负责创建实现了org.apache.cataline.Request接口的request对象
3 负责创建实现了org.apache.cataline.Response接口的response对象
这里默认的连接器的原理非常easy,就是等待http请求,创建request与response对象,然后剩下任务交给Container接口的invoke方法处理。(这里是命令模式,详细问题咱们后面谈)
public void invoke(org.apache.cataline.Request request,org.apache.cataline.Response response);
http1.1的新特性
1持久连接
2块编码
3状态码100
这三点在本章的连接器中也有体现,但本人认为这部分不是这篇博客要说明的重点,因而只是多的作解释。

tomcat总的结构框图


事实上这部分内容是在书的前言部分,本来在这一系列读书笔记一開始的时候,我就得给朋友们聊聊这部分,但当时确实没怎么看懂,就仅仅能拖到如今了。

一个servlet容器(Container)能够相应若干个连接器(Connector)。
连接器主要干的事就是接收http请求,产生request与response,然后将处理过程交给容器;
容器干的事就是调用对应的servlet对象(准确的说是调用servlet的service方法,就用传过来的request与response作參数)

Connector接口

类图例如以下



注意,与第三章不同,HttpConnector与HttpProcessor是一对多的关系,为什么?用多线程,有效率,一个HttpProcessor仅仅能同一时候处理一个http请求。这也是与第三章不同的地方。
接口情况例如以下

依据上文,大家也应该能猜出来,我在图中标示的几个方法也就是接口里面最重要的。

HttpConnector类

它既实现了上面说的Connector接口,也实现了Lifecycle接口(这个接口,我们后面再细谈)
与第三章不同,我们这里的HttpConnector类多了三个功能
1创建server套接字
在连接器初始化的时候,会调用HttpConnector类的open方法来填充成员变量serverSocket。
open内部是通过先产生一个DefaultServerSocketFactory,在从工厂里依照port号及acceptCount(本connector能同一时候支持的http请求)来创建ServerSocket对象(事实上这里我也不明确为什么不直接new出那个socket 仅仅是为了使用者 生产者分离吗)
2维护httpprocess实例
我们刚才说了一个connector关联着多个httpprocessor对象。这些对象是以Stack的数据结构存储的(先进后出)
 private Stack processors = new Stack();

  private HttpProcessor createProcessor() {

        synchronized (processors) {
            if (processors.size() > 0)
                return ((HttpProcessor) processors.pop());
            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
                return (newProcessor());
        }
            } else {
                if (maxProcessors < 0) {
                    return (newProcessor());
                } else {
                    return (null);
                }
            }
        }
    }

上面产生一个新processor的方法中有两个參数,maxProcessor是connector支持的最大prcessor量,还有一个是眼下的process量。上面的逻辑意义不解释。两个參数都是能够通过set方法设置的。
3提供http请求服务
与前面几章的类似,基本的工作都在run方法内部
public void run() {
        // Loop until we receive a shutdown command
        while (!stopped) {
            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                socket = serverSocket.accept();
               ....


熟悉的accept方法
然后就是
    HttpProcessor processor = createProcessor(); //分线程
            if (processor == null) {
                try {
                    
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;                   //话说这个continue是干什么的?
            }
            processor.assign(socket);          //主线程


须要注意的是,上面的代码我做了凝视,有两个线程了,因此processor.assign仅仅会在主线程中调用一下,不须要等待解析的结果,详细的解析在还有一个分线程里了,这样做的结果就是能够同一时候处理多个http请求。
仅仅有createProcessor为什么就产生了新的线程,由于
 private HttpProcessor newProcessor() {

        //        if (debug >= 2)
        //            log("newProcessor: Creating new processor");
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        if (processor instanceof Lifecycle) {
            try {
                ((Lifecycle) processor).start();
            } catch (LifecycleException e) {
                log("newProcessor", e);
                return (null);
            }
        }
        created.addElement(processor);
        return (processor);

    }



    答案就在  ((Lifecycle) processor).start();

HttpProcessor类


既然上面都说了creatprcessor方法会启动一个新的线程,并且也出现了异步的assign方法,那么咱们就看看HttpProcessor类的assign与run方法。

 
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);      //将自己又一次push进stack里
        }

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");

    }


    在run里面又有一个await方法,看看
   
  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);
    }


里面有个available,似乎是关键,再看看
    /**
     * Is there a new socket available?
     */
    private boolean available = false;

我就说一句,最開始available是false,我们creatProcessor时,调用了run方法,又调用了await方法,由于available为false,while循环起作用了,调用object的wait()这个分线程就停止在这了。
 processor.assign(socket);          //主线程
 看这个代码,再看assign(),available为false,跳过循环,将available改为true,(堵塞自己)然后通知全部进程。
 run里的await方法由于available为true得以执行,接着就是 process(socket);


 Request对象

默认连接器中的request对象是org.apache.cataline.Request接口的实例,uml类图例如以下



Response对象

uml类图例如以下




处理请求

这部分确实比較麻烦,能够理解为总体就是一个大循环,当httpprocessor实例终止,或者链接断开停止循环。
下来就是对request对象与response对象的初始化工作
然后是parseConnection(),parseRequest(),parseHeaders()这三个方法分别解析连接,请求与请求头。
当中解析请求与第三章的内容差点儿相同,对请求头的解析使用了字符数组,避免了代价高昂的字符串操作。
完毕解析后,process方法会把request与response对象作为參数传递给servlet容器的invoke方法
connector.getContainer().invoke(request, response);


简单的Container应用程序

如今再说说Container,我们使用的实现类是SimpleContainer(已经移除了StaticResourceProcessor类,因此不能再訪问静态资源)
我们仅仅看看他的invoke方法
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) {
        e.printStackTrace();
      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());
    }
  }

  确实,假设你看了书的前三章,就会发现这里确实没有什么可说的,非常easy。

  Bootstrap

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();          //注意这里还仅仅是单纯的函数调用,眼下跟线程还没关系,还没run呢

      // make the application wait until we press any key.
      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}


初始化部分包括了一下lifecircle的问题,不用太在意,以后我们会说。


下一节我们说说tomcat里面的命令模式


博客中图片均来自how tomcat works一书


原文地址:https://www.cnblogs.com/lcchuguo/p/4025005.html