`
ryanflyer
  • 浏览: 101080 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

How tomcat works 第四章学习笔记(1)

 
阅读更多

Tomcat4默认连接器

tomcat连接器是一个独立的模块,可以插入到一个servlet容器。一个tomcat连接器必须符合以下要求:

 

  • 必须实现org.apache.catalina.Connector接口
  • 必须创建一个实现org.apache.catalina.Request接口的request对象
  • 必须创建一个实现org.apache.catalina.Response接口的response对象

Container接口的invoke方法:

 

    /**
     * Process the specified Request, and generate the corresponding Response,
     * according to the design of this particular Container.
     *
     * @param request Request to be processed
     * @param response Response to be produced
     *
     * @exception IOException if an input/output error occurred while
     *  processing
     * @exception ServletException if a ServletException was thrown
     *  while processing this request
     */
    public void invoke(Request request, Response response)
        throws IOException, ServletException;

 在invoke方法中,容器加载servlet类、调用service方法、管理session、记录错误信息日志等等。

 

使用对象池来降低复杂对象的创建开销。

 

*HTTP1.1新特性

 

  • 持久连接

在HTTP1.0中,每对Request/Response都使用一个新的连接。

HTTP 1.1则支持Persistent Connection, 并且默认使用persistent connection.

connection: keep-alive

  • 块编码

HTTP1.1支持chunked transfer,所以可以有Transfer-Encoding头部域,HTTP1.0则没有。

Transfer-Encoding: chunked

 

 

1D\r\n 
I'm as helpless as a kitten u 
9\r\n
p a tree. 
0\r\n
  • 状态100的使用

100 (Continue) 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。

客户端在Request头部中包含Expect: 100-continue

Server看到之后呢如果回100 (Continue) 这个状态代码,客户端就继续发request body。

HTTP/1.1 100 Continue

 

HttpConnector类

(1)如何创建一个server socket?

(2)如何维护HttpProcessor池?

(3)如何处理Http请求?

org.apache.catalina.connetor.http.HttpConnector类,实现了org.apache.catalina.Connector、java.lang.Runnable和org.apache.catalina.LifeCycle接口。LifeCycle接口用来维护每一个实现了此接口的Catalina组件的生命周期。

 

  • 创建一个server socket

 

Java代码
  1. private ServerSocket open() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException,  
  2.     UnrecoverableKeyException, KeyManagementException {  
  3.   
  4.     // Acquire the server socket factory for this Connector  
  5.     ServerSocketFactory factory = getFactory();  
  6.   
  7.     // If no address is specified, open a connection on all addresses  
  8.     if (address == null) {  
  9.         log(sm.getString("httpConnector.allAddresses"));  
  10.         try {  
  11.             return (factory.createSocket(port, acceptCount));  
  12.         } catch (BindException be) {  
  13.             throw new BindException(be.getMessage() + ":" + port);  
  14.         }  
  15.     }  
  16.   
  17.     // Open a server socket on the specified address  
  18.     try {  
  19.         InetAddress is = InetAddress.getByName(address);  
  20.         log(sm.getString("httpConnector.anAddress", address));  
  21.         try {  
  22.             return (factory.createSocket(port, acceptCount, is));  
  23.         } catch (BindException be) {  
  24.             throw new BindException(be.getMessage() + ":" + address + ":" + port);  
  25.         }  
  26.     } catch (Exception e) {  
  27.         log(sm.getString("httpConnector.noAddress", address));  
  28.         try {  
  29.             return (factory.createSocket(port, acceptCount));  
  30.         } catch (BindException be) {  
  31.             throw new BindException(be.getMessage() + ":" + port);  
  32.         }  
  33.     }  
  34.   
  35. }  
  • 如何维护HttpProcessor池

首先采用栈来存储HttpProcessor实例,HttpProcessor池动态扩容,根据三个属性来设置:

 

curProcessors: 当前HttpProcessor实例的个数

minProcessors : int 初始化时,最小的HttpProcessor实例个数

maxProcessors:最大HttpProcessor实例个数,当小于0时,不做限制

 

初始化时创建最小HttpProcessor实例个数代码:

 

 

Java代码
  1. // Create the specified minimum number of processors  
  2.         while (curProcessors < minProcessors) {  
  3.             if ((maxProcessors > 0) && (curProcessors >= maxProcessors))  
  4.                 break;  
  5.             HttpProcessor processor = newProcessor();  
  6.             recycle(processor);  
  7.         }  
 

 

 

  • 处理http请求

HttpConnector类在自己的run方法中有自己的主要逻辑,直到HttpConnetor停止之前都会一直等待接收http请求。对于每一个http请求,通过调用createProcessor方法获得一个HttpProcessor实例。然而,大多数时间,createProcessor方法并不会创建一个新的HttpProcessor对象,而是从一个HttpProcessor池中获取。如果在这个池中(实际采用的是堆栈来存储)有一个HttpProcessor实例可供使用,执行出栈操作。如果栈为空且仍然没有超过HttpProcessor实例的最大个数,则创建一个新的HttpProcessor实例,否则,createProcessor方法将返回null,此时socket执行关闭操作而不会响应到来的http请求。如果createProcessor方法没有返回null,客户端的socket传递给HttpProcessor的assign方法。

 

Java代码
  1. private HttpProcessor createProcessor() {  
  2.   
  3.        synchronized (processors) {  
  4.            if (processors.size() > 0) {  
  5.                return ((HttpProcessor) processors.pop());  
  6.            }  
  7.            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {  
  8.                return (newProcessor());  
  9.            } else {  
  10.                if (maxProcessors < 0) {  
  11.                    return (newProcessor());  
  12.                } else {  
  13.                    return (null);  
  14.                }  
  15.            }  
  16.        }  
  17.   
  18.    }  

  HttpProcessor类

 

在本章,我们最感兴趣的是HttpProcessor类如何使assign方法异步以便HttpConnetor实例可以同时为多个http请求服务。HttpProcessor类中另外一个重要的方法是私有方法process方法,它解析了http请求并且调用了容器的invoke方法。

第三章中,HttpConnetor在自己的线程中运行,然而它必须等待当前处理的http请求结束之后才可以处理下一个请求。

第三章的HttpConnetor类的run方法代码如下:

 

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

 HttpProcessor类的process方法在第三章中是同步方法。因此,它的run方法等待直到process方法处理结束才接收下一个请求。在本章,默认的连接器的HttpProcessor类实现了Runnable接口,因此每一个HttpProcessor实例都运行在自己的线程中,我们称为“processor线程”

 

Lifecycle接口的start和stop方法

 

 

    /**
     * Prepare for the beginning of active use of the public methods of this
     * component.  This method should be called before any of the public
     * methods of this component are utilized.  It should also send a
     * LifecycleEvent of type START_EVENT to any registered listeners.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    public void start() throws LifecycleException;


    /**
     * Gracefully terminate the active use of the public methods of this
     * component.  This method should be the last one called on a given
     * instance of this component.  It should also send a LifecycleEvent
     * of type STOP_EVENT to any registered listeners.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that needs to be reported
     */
    public void stop() throws LifecycleException;

 

 

HttpProcessor实现了Lifecycle接口,因此HttpProcessor类的start方法代码如下:

 

    /**
     * 判断HttpProcessor组件是否启动
     */
    private boolean started = false;
/**
     * Start the background thread we will use for request processing.
     *
     * @exception LifecycleException if a fatal startup error occurs
     */
    public void start() throws LifecycleException {

        if (started)
            throw new LifecycleException
                (sm.getString("httpProcessor.alreadyStarted"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        threadStart();

    }
 

 

 

    /**
     * 开启后台处理线程
     */
    private void threadStart() {

        log(sm.getString("httpProcessor.starting"));

        thread = new Thread(this, threadName);
        thread.setDaemon(true);
        thread.start();

        if (debug >= 1)
            log(" Background thread has been started");

    }

   HttpProcessor的run方法的while循环执行流程:获得一个socket,处理它,调用connector的recycle(回收)方法把当前的HttpProcessor实例压回栈中。注意到while循环中停止到await方法处,await方法掌握着“processor thread”的控制流,直到它从HttpConnetor获得到一个新的socket对象。换句话说,直到HttpConnetor类调用HttpProcessor实例的assign方法。

 

            HttpProcessor processor = createProcessor();
            processor.assign(socket);

 HttpProcessor的run方法代码如下:

   /* * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    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();
        }

    }

将调用完的HttpProcessor实例压回栈中的代码实现:

 

  void recycle(HttpProcessor processor) {
        processors.push(processor);

    }

然而,await方法和assign方法运行在不同的线程中,assign方法是在HttpConnetor的run方法中被调用的,即“connector线程”。

那么assign方法是如何告诉await方法它被调用了呢?

利用一个布尔型变量available和java.lang.Object类的wait和notifyAll方法。

 

注:Object的wait方法导致当前线程等待直到其他线程对这个对象调用notify或者nitifyAll方法。

 

connetor线程调用的assign方法:

 

 

    /**
     * Process an incoming TCP/IP connection on the specified socket.  Any
     * exception that occurs during processing must be logged and swallowed.
     * <b>NOTE</b>:  This method is called from our Connector's thread.  We
     * must assign it to our own thread so that multiple simultaneous
     * requests can be handled.
     *
     * @param socket TCP socket to process
     */
    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");

    }

 HttpProcessor线程调用的await方法:

 

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

    }

 初始时,当“processor thread”刚启动时,available为false,即还没有可用的socket。因此线程在while循环中等待,直到其他线程调用notify或者notifyAll方法。也就是说,调用wait方法导致“processor thread”暂停直到“connector thread”对这个HttpProcessor实例调用notifyAll方法。当一个新的socket被分配,“connector thread”调用HttpProcessor的assign方法。avilable置为true,唤醒“processor thread”。

 

为什么await方法需要使用一个本地变量(socket)且不返回这个socket变量的实例?

 

因为HttpProcessor实例的socket变量在当前socket处理完之前还可以分配给下一个到来的socket。

 

为什么await方法需要调用notifyAll?

为了当另外一个socket到达的时候此时available为true,这时候,“connector thread”将会停止里面的assign方法直到收到“processor thread”的notifyAll方法。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics