zoukankan      html  css  js  c++  java
  • 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一书


  • 相关阅读:
    字典树Trie
    转载一个不错的LRU cache
    git和github基础入门
    git基础之常用操作
    python矩阵和向量的转置问题
    梯度下降法注意要点
    python 浮点数问题
    Python数据分析基础——读写CSV文件2
    Python数据分析基础——读写CSV文件
    读书笔记----javascript函数编程
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4025005.html
Copyright © 2011-2022 走看看