tomcat(4)Tomcat的默认连接器

【0】README
0.0)本文部分文字描述转自:“深入剖析tomcat”,旨在学习  tomat(4)Tomat的默认连接器 的基础知识;
0.1)Tomcat中的连接器是一个独立的模块,可以插入到servlet容器中,而且还有很多连接器可以使用;
0.2)Tomcat中使用的连接器必须满足如下要求(requirement):
r1)实现 org.apache.catalina.Connector 接口;
r2)负责创建实现了 org.apache.catalina.Request接口的request对象;
r3)负责创建实现了 org.apache.catalina.Response接口的response对象;
0.3)连接器会等待HTTP请求,以创建request 和 response对象,然后调用 org.apache.catalina.Container接口的 invoke方法,将request对象和response对象传给 servlet容器。invoke() 方法签名如下: (干货——这里涉及到类加载器)
public void invoke(Request request, Response response) // SimpleContainer.invoke
    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("servlet." + servletName);
    }
    catch (ClassNotFoundException e) {
      System.out.println(e.toString());
    }

    Servlet servlet = null;

    try {
      servlet = (Servlet) myClass.newInstance(); // 创建请求URI对应的servlet实例 
      servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
    }
    ......
  }
0.3.1)invoke方法内部:servlet容器会载入相应的servlet类,调用其 service() 方法,管理session对象,记录错误消息等操作;
0.3.2)Tomcat4的默认连接器使用了一些优化方法:一、使用了一个对象池来避免了频繁创建对象带来的性能损耗;二、在很多地方,Tomcat4 的默认连接器使用了字符数组来代替字符串; (干货——Tomcat4的默认连接器使用了一些优化方法
0.4)for complete source code, please visit  https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter4
0.5)本文讲解的是 tomcat中的默认连接器,而 这篇博文 tomcat(3)连接器 讲解的是以前的前辈自定义的连接器(功能和tomcat默认连接器类似),这旨在学习 tomcat中默认连机器的原理。
0.6)温馨建议:建议阅读本文之前,已阅读过 tomcat(1~3)的系列文章,因为它们是环环相扣的;

【1】HTTP 1.1 的新特性
【1.1】持久连接
1)intro to persistent connect:在HTTP1.1之前, 无论浏览器何时连接到web server,当server返回请求资源后,会断开与浏览器的连接,但网页上会包含一些其他资源,如图片;所以,当请求一个页面时,浏览器还需要下载这些被页面引用的资源。如果页面和它引用的所有资源文件都使用不同的连接进行下载的话,处理过程会很慢;这也就是为什么会引入持久连接; (干货——引入持久连接的原因)
1.1)使用持久连接后,当下载了页面后,server并不会立即关闭连接,相反,它会等待web client请求被该页面引用的所有资源。这样一来,页面和被引用的资源都会使用同一个连接来下载。考虑到建立/关闭 HTTP 连接是一个系统开销很大的操作,使用同一个连接来下载所有的资源会为web server, client 和 网络节省很多时间和工作量;
1.2)如何默认使用持久连接:浏览器发送如下的请求头信息: connection: keep-alive

【1.2】块编码
1)建立持久连接后,server 可以从多个资源发送字节流,而客户端也可以使用该连接发送多个请求。这样的结果就是发送方必须在每个请求或响应中添加 content-length 头信息。这样,接收方才知道如何解释这些字节信息。但发送方通常不知道要发送多少字节,所以必须有一种方法来告诉接收方在不知道发送内容长度的case下,如何解析已经接受到的内容;
2)HTTP1.1 使用一个名为 "transfer-encoding" 的特殊请求头,来指明字节流将会分块发送。对每一个块,块的长度(以16进制表示)后面会有一个回车/换行符(CR/LF ),然后是具体的数据;一个事务以一个长度为0的块标记。
2.1)看个荔枝:若要用两个块发送下面38个字节的内容,其中一块为29个字节,第2个块为9个字节:(干货——块编码的一个荔枝)
I'm as helpless as a kitten up a tree.
那么实际上应该发送如下内容:
1D\r\n
I'm as helpless as a kitten u
9\r\n
p a tree.
0\r\n
1D 的10进制表示是29, 表明第一个块的长度是29个字节,\0\r\n 表明事务已经完成;
【1.3】状态码100的使用 
1)使用HTTP1.1的client 可以在向server发送请求体之前发送如下的请求头,并等待server的确认:
Expect: 100-continue  //客户端发送
2)server接收到 “Expect: 100-continue”请求头后,若它可以接收并处理该请求时,发送如下响应头:
HTTP/1.1 100 continue
//注意,返回内容后面要加上 CRLF 字符。然后server 继续读取输入流的内容。

【2】Connector接口(Tomcat的连接器必须实现的接口)
1)该接口中声明了很多方法,最重要的是 getContainer(), setContainer(), createRequest(), createResponse() 方法;

【3】HttpConnector类(实现了Connector, Runnable, Lifecycle 接口)
public final class HttpConnector
    implements Connector, Lifecycle, Runnable {
1)Lifecycle接口:用于维护每个实现了 该接口的每个Catalina组件的生命周期;
2)HttpConnector实现了Lifecycle接口:因此当创建一个 HttpConnector实例后,就应该调用其initialize方法和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();
    }
  }
}
【3.1】创建服务器套接字
1)HttpConnector类的 initialize方法:会调用一个私有方法open(),后者返回一个java.net.ServerSocket实例,赋值给成员变量serverSocket,它是通过open方法从一个服务器套接字工厂得到这个实例;
 
    public void initialize() // <span style="font-family: 宋体; font-size: 16px;  line-height: 24px;"><strong>HttpConnector.initialize()</strong></span>
    throws LifecycleException {
        if (initialized)
            throw new LifecycleException (
                sm.getString("httpConnector.alreadyInitialized"));

        this.initialized=true;
        Exception eRethrow = null;

        // Establish a server socket on the specified port
        try {
            serverSocket = open();
        } catch (IOException ioe) {
            log("httpConnector, io problem: ", ioe);
            eRethrow = ioe;
        } catch (KeyStoreException kse) {
            log("httpConnector, keystore problem: ", kse);
            eRethrow = kse;
        } catch (NoSuchAlgorithmException nsae) {
            log("httpConnector, keystore algorithm problem: ", nsae);
            eRethrow = nsae;
        } catch (CertificateException ce) {
            log("httpConnector, certificate problem: ", ce);
            eRethrow = ce;
        } catch (UnrecoverableKeyException uke) {
            log("httpConnector, unrecoverable key: ", uke);
            eRethrow = uke;
        } catch (KeyManagementException kme) {
            log("httpConnector, key management problem: ", kme);
            eRethrow = kme;
        }

        if ( eRethrow != null )
            throw new LifecycleException(threadName + ".open", eRethrow);

    }
 private ServerSocket open() // HttpConnector.open()
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException
    {

        // Acquire the server socket factory for this Connector
        ServerSocketFactory factory = getFactory();

        // If no address is specified, open a connection on all addresses
        if (address == null) {
            log(sm.getString("httpConnector.allAddresses"));
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

        // Open a server socket on the specified address
        try {
            InetAddress is = InetAddress.getByName(address);
            log(sm.getString("httpConnector.anAddress", address));
            try {
                return (factory.createSocket(port, acceptCount, is));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + address +
                                        ":" + port);
            }
        } catch (Exception e) {
            log(sm.getString("httpConnector.noAddress", address));
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

    }
【3.2】维护 HttpProcessor 实例
1)在Tomcat默认的连接器中: HttpProcessor实例有一个HttpProcessor 对象池,每个HttpProcessor 实例都运行在其自己的线程中。这样,HttpConnector实例就可以同时处理多个 HTTP请求了; (干货——HttpProcessor 对象池就是一个Stack,并不是Vector
 private Stack processors = new Stack(); // HttpConnector.java
    void recycle(HttpProcessor processor) { // HttpConnector.java

        //        if (debug >= 2)
        //            log("recycle: Recycling processor " + processor);
        processors.push(processor);

    }
private HttpProcessor createProcessor() {

        synchronized (processors) {
            if (processors.size() > 0) {
                // if (debug >= 2)
                // log("createProcessor: Reusing existing processor");
                return ((HttpProcessor) processors.pop()); // attend for this line.
            }
            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
                // if (debug >= 2)
                // log("createProcessor: Creating new processor");
                return (newProcessor());
            } else {
                if (maxProcessors < 0) {
                    // if (debug >= 2)
                    // log("createProcessor: Creating new processor");
                    return (newProcessor());
                } else {
                    // if (debug >= 2)
                    // log("createProcessor: Cannot create new processor");
                    return (null);
                }
            }
        }
    }

补充
Collection体系
|--:List元素是有序的,元素可以重复,因为该集合体系有索引
    |--:ArrayList:底层的数据结构使用的是数组结构,特点:查询速度很快,但是增删稍慢,线程不同步
    |--:LinkedList:底层使用的是链表数据结构,特点:增删速度很快,查询稍慢
    |--:Vector:底层是数组数据结构,线程同步,被ArrayList替代了
|--:Set  元素是无序的,元素不可以重复
为什么要用 ArrayList 取代 Vector呢? 因为, Vector 类的所有方法都是同步的(可以由两个线程安全的访问一个 Vector对象);但是, 如果由一个线程访问 Vector, 代码需要在同步操作上 耗费大量的时间; (干货——ArrayList非同步访问,而Vector同步访问); 而ArryaList 不是同步 的, 所以, 建议在不需要同步时 使用 ArrayList, 而不要使用 Vector;
补充over
2)在HttpConnector中:创建的HttpProcessor 实例的个数由两个变量决定:minProcessors 和 maxProcessors;
2.1)初始,HttpConnector对象会依据minProcessors的数值来创建 HttpProcessor实例:若是请求的数目超过了HttpProcessor 实例所能处理的范围,HttpConnector 实例就会创建更多的HttpProcessor实例,直到数目达到  maxProcessors 限定的范围;
2.2)若希望可以持续地创建 HttpProcessor实例: 就可以将 maxProcessors 设定为负值;
2.3)看个代码:下面的代码是在 HttpConnector 类中的start方法中创建初始数量的 HTTPProcessor实例的部分实现: (干货——HttpConnector.start方法调用了HttpConnector.newProcessor方法,而HttpConnector.newProcessor方法调用了HttpProcessor.start方法)
     public void start() throws LifecycleException { // HttpConnector.start()

        // Validate and update our current state
        if (started)
            throw new LifecycleException
                (sm.getString("httpConnector.alreadyStarted"));
        threadName = "HttpConnector[" + port + "]";
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        // Start our background thread
        threadStart(); // invoke run method.

        // Create the specified minimum number of processors
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            HttpProcessor processor = newProcessor();
            recycle(processor);
        }
    }
      private HttpProcessor newProcessor() { // H<span style="font-family: 宋体;">ttpConnector.newProcessor()</span>

        //        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);  // created is a Vector Collection.
        return (processor);
    } 
     private Vector created = new Vector();
 void recycle(HttpProcessor processor) { // HttpContainer.recycle()

        //        if (debug >= 2)
        //            log("recycle: Recycling processor " + processor);
        processors.push(processor);

    }
 public void stop() throws LifecycleException {

        // Validate and update our current state
        if (!started)
            throw new LifecycleException
                (sm.getString("httpConnector.notStarted"));
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;

        // Gracefully shut down all processors we have created
        for (int i = created.size() - 1; i >= 0; i--) {
            HttpProcessor processor = (HttpProcessor) created.elementAt(i); // this line.
            if (processor instanceof Lifecycle) {
                try {
                    ((Lifecycle) processor).stop();
                } catch (LifecycleException e) {
                    log("HttpConnector.stop", e);
                }
            }
        }

        synchronized (threadSync) {
            // Close the server socket we were using
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    ;
                }
            }
            // Stop our background thread
            threadStop();
        }
        serverSocket = null;
    }
Attention)
A0)显然变量created是private类型:其在HttpConnector.java 中出现了3次,一次是  private Vector created = new Vector();, 一次是created.addElement(processor);还有一次是stop方法中(上述最后一段代码)(干货——但,HttpProcessor的对象池不是Vector而是Stack)
A1)其中,newProcessor方法负责创建 HttpProcessor 实例,并将 curProcessors 加1;recycle() 方法将创建的HttpProcessor 对象入栈;
A2)每个 HttpProcessor 实例负责解析HTTP请求行和请求头, 填充 request对象;
    void recycle(HttpProcessor processor) {  // 

        //        if (debug >= 2)
        //            log("recycle: Recycling processor " + processor);
        processors.push(processor); // <span style="font-weight: bold; font-family: 宋体;">processors  is a stack.</span>
    }
    private Stack processors = new Stack(); // HttpConnector.java
【3.3】提供HTTP请求服务
1)HttpConnector类的主要业务逻辑在其run() 方法中。run() 方法包含一个 while循环,在该循环体内,服务器套接字等待 HTTP请求,直到HttpConnector对象关闭;
public void run() { // HttpConnector.run()

        // Loop until we receive a shutdown command
        while (!stopped) {

            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                //                if (debug >= 3)
                //                    log("run: Waiting on serverSocket.accept()");
                socket = serverSocket.accept();
                //                if (debug >= 3)
                //                    log("run: Returned from serverSocket.accept()");
                if (connectionTimeout > 0)
                    socket.setSoTimeout(connectionTimeout);
                socket.setTcpNoDelay(tcpNoDelay);
            } catch (AccessControlException ace) {
                log("socket accept security exception", ace);
                continue;
            } catch (IOException e) {
                //                if (debug >= 3)
                //                    log("run: Accept returned IOException", e);
                try {
                    // If reopening fails, exit
                    synchronized (threadSync) {
                        if (started && !stopped)
                            log("accept error: ", e);
                        if (!stopped) {
                            //                    if (debug >= 3)
                            //                        log("run: Closing server socket");
                            serverSocket.close();
                            //                        if (debug >= 3)
                            //                            log("run: Reopening server socket");
                            serverSocket = open();
                        }
                    }
                    //                    if (debug >= 3)
                    //                        log("run: IOException processing completed");
                } catch (IOException ioe) {
                    log("socket reopen, io problem: ", ioe);
                    break;
                } catch (KeyStoreException kse) {
                    log("socket reopen, keystore problem: ", kse);
                    break;
                } catch (NoSuchAlgorithmException nsae) {
                    log("socket reopen, keystore algorithm problem: ", nsae);
                    break;
                } catch (CertificateException ce) {
                    log("socket reopen, certificate problem: ", ce);
                    break;
                } catch (UnrecoverableKeyException uke) {
                    log("socket reopen, unrecoverable key: ", uke);
                    break;
                } catch (KeyManagementException kme) {
                    log("socket reopen, key management problem: ", kme);
                    break;
                }

                continue;
            }

            // Hand this socket off to an appropriate processor
            HttpProcessor processor = createProcessor();
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }
            //            if (debug >= 3)
            //                log("run: Assigning socket to processor " + processor);
            processor.assign(socket);

            // The processor will recycle itself when it finishes

        }

        // Notify the threadStop() method that we have shut ourselves down
        //        if (debug >= 3)
        //            log("run: Notifying threadStop() that we have shut down");
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }
2)对于每个引入的HTTP请求,它通过 调用其私有方法 createProcessor() 获得一个HttpProcessor对象;
HttpProcessor processor = createProcessor(); // invoked by HttpContainer.run() method.
private Stack processors = new Stack(); // you should know the processors is a Stack.
     private HttpProcessor createProcessor() { // HttpConnector.createProcessor() 

        synchronized (processors) {
            if (processors.size() > 0) {
                // if (debug >= 2)
                // log("createProcessor: Reusing existing processor");
                return ((HttpProcessor) processors.pop());
            }
            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
                // if (debug >= 2)
                // log("createProcessor: Creating new processor");
                return (newProcessor());
            } else {
                if (maxProcessors < 0) {
                    // if (debug >= 2)
                    // log("createProcessor: Creating new processor");
                    return (newProcessor());
                } else {
                    // if (debug >= 2)
                    // log("createProcessor: Cannot create new processor");
                    return (null);
                }
            }
        }

    }<span style="font-family: 宋体;"> </span>

3)若createProcessor() 方法的返回值不是null,则会将客户端套接字传入到 HttpProcessor类的 assign()方法中:
 // Hand this socket off to an appropriate processor
            HttpProcessor processor = createProcessor(); // these code are located in HttpConnector.run() 
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }
            //            if (debug >= 3)
            //                log("run: Assigning socket to processor " + processor);
            processor.assign(socket); // this line, invoked by HttpConnector.run method.

            // The processor will recycle itself when it finishes

        }

        // Notify the threadStop() method that we have shut ourselves down
        //        if (debug >= 3)
        //            log("run: Notifying threadStop() that we have shut down");
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
 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");

    }
4)现在,HttpProcessor实例的任务是读取套接字的输入流,解析HTTP请求。这里需要注意的是,assign方法直接返回,而不需要等待 HttpProcessor 实例完成解析,这样 HttpContainer才能持续服务传入的 HTTP请求,而 HttpProcessor 实例是在其自己的线程中完成解析工作的;

【4】HttpProcessor类(主要讲解该类的assign方法的异步实现)
0)HttpProcessor类中另一个重要的方法是其私有方法process() 方法:该方法负责解析 HTTP请求,并调用相应的servlet容器的invoke方法;
private void process(Socket socket) {

        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        keepAlive = true;

        while (!stopped && ok && keepAlive) {

            finishResponse = true;

            try {
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                ((HttpServletResponse) response.getResponse()).setHeader
                    ("Server", SERVER_INFO);
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }

            // Parse the incoming request
            try {
                if (ok) {
                    parseConnection(socket);
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }
                }
            } catch (EOFException e) {
                // It's very likely to be a socket disconnect on either the 
                // client or the server
                ok = false;
                finishResponse = false;
            } catch (ServletException e) {
                ok = false;
                try {
                    ((HttpServletResponse) response.getResponse())
                        .sendError(HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
            } catch (InterruptedIOException e) {
                if (debug > 1) {
                    try {
                        log("process.parse", e);
                        ((HttpServletResponse) response.getResponse())
                            .sendError(HttpServletResponse.SC_BAD_REQUEST);
                    } catch (Exception f) {
                        ;
                    }
                }
                ok = false;
            } catch (Exception e) {
                try {
                    log("process.parse", e);
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }

            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    connector.getContainer().invoke(request, response); // process method invokes the invoke method of corresponding servlet container
                }
            } catch (ServletException e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            } catch (InterruptedIOException e) {
                ok = false;
            } catch (Throwable e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }

            // We have to check if the connection closure has been requested
            // by the application or the response stream (in case of HTTP/1.0
            // and keep-alive).
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }

            // End of request processing
            status = Constants.PROCESSOR_IDLE;

            // Recycling the request and the response objects
            request.recycle();
            response.recycle();

        }

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;

    }
1)在Tomcat的默认连接器中,HttpProcessor类实现了 java.lang.Runnable 接:这样,每个HttpProcessor实例就可以运行在自己的线程中了,称为“处理器线程”。为每个 HttpProcessor 对象创建 HttpProcessor 实例后,会调用其start方法,启动HttpProcessor 实例的处理器线程;(下面是 HttpProcessor类中的run方法的实现) (干货——注意区分开HttpProcessor.run方法和 HttpConnector.run方法 )
     public void run() { // HttpProcessor.run()

        // Process requests until we receive a shutdown signal
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await(); // this line, <span style="font-family: 宋体; font-size: 16px; line-height: 24px;">run方法的while循环执行到 await方法时会阻塞</span>
            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();
        }

    }
private synchronized Socket await() { // HttpProcessor.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);

    }
对以上代码的分析(Analysis):
A1)run方法中的while循环做如下几件事情:获取套接字对象,进行处理,调用连接器的recycle() 方法将当前的 HttpRequest实例压回栈中。
A2)下面是 HttpConnector类中 recycle方法的代码: 
 */
    void recycle(HttpProcessor processor) {

        //        if (debug >= 2)
        //            log("recycle: Recycling processor " + processor);
        processors.push(processor);

    }
A3)run方法的while循环执行到 await方法时会阻塞。await方法会阻塞处理器线程的控制流,直到它从HttpConnector中获得了新的Socket对象。即,直到HttpConnector对象调用 HttpProcessor实例的assign方法前,都会一直阻塞; 但是,await方法 和assign方法并不是运行在同一个线程中的。assign方法是从 HttpConnector对象的 run方法中调用的。我们称 HttpConnector实例中run方法运行时所在的线程为“连接器线程”。通过使用available的布尔变量和 Object.wait() 方法 和 notifyAll() 方法来进行沟通的;
 /**
     * Await a newly assigned Socket from our Connector, or <code>null</code>
     * if we are supposed to shut down.
     */
    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);

    }
Attention)
A1)wait方法会使当前线程进入等待状态,直到其他线程调用了这个对象的notify() 方法和notifyAll方法;
A2)下表总结了 await方法和 assign方法的程序流;

Why)
W1)为什么 await方法要使用一个局部变量 socket,而不直接将成员变量socket返回呢? 因为使用局部变量可以在当前Socket对象处理完之前,继续接收下一个Socket对象;
W2)为什么 await方法需要调用notifyAll方法呢?是为了防止出现另一个Socket对象以及到达,而此时变量available的值还是true的case。在这种case下,连接器线程会在assign方法内的循环中阻塞,知道处理器线程调用了 notifyAll方法;

【5】Request对象(继承了 RequestBase)
【6】Response对象
【7】处理请求(重点讨论 HttpProcessor类的 process方法)
1)process方法执行以下3个操作: 解析连接, 解析请求, 解析请求头
2)intro to process method:
2.1)通过在Connector接口中设置缓冲区的大小,任何人都可以使用连接器来设置缓冲区的大小;
private void process(Socket socket) { // HttpProcessor.process()

        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        keepAlive = true;

        while (!stopped && ok && keepAlive) {

            finishResponse = true;

            try {
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                ((HttpServletResponse) response.getResponse()).setHeader
                    ("Server", SERVER_INFO);
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }

            // Parse the incoming request
            try {
                if (ok) {
                    parseConnection(socket); // step1:解析连接
                    parseRequest(input, output); // step2:解析请求
                    if (!request.getRequest().getProtocol() 
                        .startsWith("HTTP/0"))
                        parseHeaders(input); // step3:解析请求头
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }
                }
            } catch (EOFException e) {
                // It's very likely to be a socket disconnect on either the 
                // client or the server
                ok = false;
                finishResponse = false;
            } catch (ServletException e) {
                ok = false;
                try {
                    ((HttpServletResponse) response.getResponse())
                        .sendError(HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
            } catch (InterruptedIOException e) {
                if (debug > 1) {
                    try {
                        log("process.parse", e);
                        ((HttpServletResponse) response.getResponse())
                            .sendError(HttpServletResponse.SC_BAD_REQUEST);
                    } catch (Exception f) {
                        ;
                    }
                }
                ok = false;
            } catch (Exception e) {
                try {
                    log("process.parse", e);
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }

            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    connector.getContainer().invoke(request, response);
                }
            } catch (ServletException e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            } catch (InterruptedIOException e) {
                ok = false;
            } catch (Throwable e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }

            // We have to check if the connection closure has been requested
            // by the application or the response stream (in case of HTTP/1.0
            // and keep-alive).
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }

            // End of request processing
            status = Constants.PROCESSOR_IDLE;

            // Recycling the request and the response objects
            request.recycle();
            response.recycle();

        }

        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;
    }
2.2)然后是while循环,在该循环内,不断读入输入流,知道HttpProcessor 实例终止,抛出一个异常,或连接断开;
// Parse the incoming request
            try {  // there code are located in HttpProcessor.process() method.
                if (ok) {
                    parseConnection(socket);
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        parseHeaders(input);
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }
                }
            } 
【7.1】解析连接
1)parseConnection() 方法:会从套接字中获取Internet地址,将其赋值给 HttpRequestImpl 对象;此外,它还要检查是否使用了代理,将Socket对象赋值给 request对象;
private void parseConnection(Socket socket) // HttpProcessor.parseConnection()
        throws IOException, ServletException {

        if (debug >= 2)
            log("  parseConnection: address=" + socket.getInetAddress() +
                ", port=" + connector.getPort());
        ((HttpRequestImpl) request).setInet(socket.getInetAddress());
        if (proxyPort != 0)
            request.setServerPort(proxyPort);
        else
            request.setServerPort(serverPort);
        request.setSocket(socket);

    }
【7.2】解析请求
【7.3】解析请求头
1)默认连接器中的parseHeaders() 方法:使用了 org.apache.catalina.connector.http 包内的HttpHeader类和 DefaultHeader类。HttpHeader类表示一个HTTP 请求头。HttpHeader 类使用了字符数组来避免高代价的字符串操作。DefaultHeaders类是一个final类,包含了 字符数组形式的标准HTTP请求头:
final class DefaultHeaders {


    // -------------------------------------------------------------- Constants


    static final char[] AUTHORIZATION_NAME = "authorization".toCharArray();
    static final char[] ACCEPT_LANGUAGE_NAME = "accept-language".toCharArray();
    static final char[] COOKIE_NAME = "cookie".toCharArray();
    static final char[] CONTENT_LENGTH_NAME = "content-length".toCharArray();
    static final char[] CONTENT_TYPE_NAME = "content-type".toCharArray();
    static final char[] HOST_NAME = "host".toCharArray();
    static final char[] CONNECTION_NAME = "connection".toCharArray();
    static final char[] CONNECTION_CLOSE_VALUE = "close".toCharArray();
    static final char[] EXPECT_NAME = "expect".toCharArray();
    static final char[] EXPECT_100_VALUE = "100-continue".toCharArray();
    static final char[] TRANSFER_ENCODING_NAME =
        "transfer-encoding".toCharArray();


    static final HttpHeader CONNECTION_CLOSE =
        new HttpHeader("connection", "close");
    static final HttpHeader EXPECT_CONTINUE =
        new HttpHeader("expect", "100-continue");
    static final HttpHeader TRANSFER_ENCODING_CHUNKED =
        new HttpHeader("transfer-encoding", "chunked");
2)parseHeaders() 方法使用while循环读取所有的HTTP 请求信息。调用request对象的 allocateHeader方法获取一个内容为空的 HttpHeader实例开始。然后该实例被传入SocketInputStream 实例的readHeader方法中:
 private void parseHeaders(SocketInputStream input) // HttpProcessor.parseHeaders()
        throws IOException, ServletException {

        while (true) {

            HttpHeader header = request.allocateHeader();

            // Read the next header
            input.readHeader(header);
            if (header.nameEnd == 0) {
                if (header.valueEnd == 0) {
                    return;
                } else {
                    throw new ServletException
                        (sm.getString("httpProcessor.parseHeaders.colon"));
                }
            }

            String value = new String(header.value, 0, header.valueEnd);
            if (debug >= 1)
                log(" Header " + new String(header.name, 0, header.nameEnd)
                    + " = " + value);

           ......
            request.nextHeader();
        }
    }
3)若所有的请求头都已经读取过了,则readHeader()方法不会再给 HttpHeader 实例设置name属性了。就退出parseHeader方法了:
if (header.nameEnd == 0) {
                if (header.valueEnd == 0) {
                    return;
                } else {
                    throw new ServletException
                        (sm.getString("httpProcessor.parseHeaders.colon"));
                }
            }
【8】简单的Container 应用程序
1)SimpleContainer类实现了 org.apache.catalina.Container接口,这样它就可以与默认连接器进行关联。 
public class SimpleContainer implements Container { // just demonstrate a core method 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("servlet." + 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());
    }
  }
Attention)我总结了一张Tomcat默认连接器测试用例的大致调用过程

 /**
     * Await a newly assigned Socket from our Connector, or <code>null</code>
     * if we are supposed to shut down.
     */
    private synchronized Socket await() { //HttpProcessor.awati() 方法

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

    }
对以上代码的分析(Analysis):
A1)HttpConnector类的大致工作:
step1)initialize方法:调用该类的open方法创建服务器套接字;
step2)start方法:开启一个线程,该线程中有一个while循环,不断接收client发送的HTTP连接请求,接着调用其类的createProcessor方法;
step3)createProcessor方法:调用其类的 newProcessor方法;
step4)newProcessor方法:创建HttpProcessor(HTTP连接器的支持类,HTTP请求处理器),利用HttpProcessor实例开启一个线程,调用 HttpProcessor.run()方法;(转向HttpProcessor类的run方法) (干货中的干货——也即当clients 发出http 连接请求后,HttpConnector 在while循环中创建HttpConnector的支持类 HttpProcessor,Http处理器类,并调用该类的start方法,开启线程,即while循环为每一个client 请求 开启一个线程进行处理,多么巧妙)
A2)HttpProcessor类的大致工作:
step1)run方法:传入套接字参数,并调用process方法;
step2)process方法:依次调用 parseConnection()方法, parseRequest()方法, parseHeader() 方法:
上述3个方法的作用参见本文章节【7】 (也可以参见下面的补充);调用上述三个方法后,会调用连接器HttpConnector实例的关联容器的invoke方法;(转向container的invoke方法)
step3)SimleContainer.invoke方法:invoke方法声明为,invoke(Request request, Response response): (见下面的代码实例)
step3.1)创建类加载器;
step3.2)利用类加载器集合request中的请求URI( HttpProcessor.parseReqeust解析出的请求URI),加载请求的servlet,创建该servlet实例并调用其service方法(service方法接着就会向client发送响应信息),ending;
public final class Bootstrap {
  public static void main(String[] args) {
    HttpConnector connector = new HttpConnector();
    SimpleContainer container = new SimpleContainer(); // 创建容器
    connector.setContainer(container); // 将该 容器 和 http连接器 相关联
    try {
      connector.initialize();
      connector.start();

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

补充-Complementary begins)
C1)HttpProcessor.parseConnection方法:会从套接字中获取internet地址,端口号,协议等信息;
C2)HttpProcessor.parseRequest方法:会解析请求体的第一行请求信息(请求方法——URI——协议/版本);
C3)HttpProcessor.parseHeader方法:解析请求体中的请求头信息,还会附加解析cookie信息;
补充-Complementary ends)

补充-Complementary2 begins)在补充一个知识点(request和response是何时创建的?——其实在HttpProcessor 构造器中就已经创建了)
 public HttpProcessor(HttpConnector connector, int id) {
        super();
        this.connector = connector;
        this.debug = connector.getDebug();
        this.id = id;
        this.proxyName = connector.getProxyName();
        this.proxyPort = connector.getProxyPort();
        this.request = (HttpRequestImpl) connector.createRequest();
        this.response = (HttpResponseImpl) connector.createResponse();
        this.serverPort = connector.getPort();
        this.threadName =
          "HttpProcessor[" + connector.getPort() + "][" + id + "]";
    }
 public Request createRequest() {        
        HttpRequestImpl request = new HttpRequestImpl();
        request.setConnector(this);
        return (request);
    }
public Response createResponse() {
        HttpResponseImpl response = new HttpResponseImpl();
        response.setConnector(this);
        return (response);
    }
补充-Complementary2 ends)

【9】运行应用程序
9.1)运行参数
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter4.startup.Bootstrap
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
from service
from service
9.2)运行结果


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值