首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > Web前端 >

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

2012-11-23 
How tomcat works 第四章学习笔记(1)Tomcat4默认连接器tomcat连接器是一个独立的模块,可以插入到一个servl

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\np 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

?

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方法。

热点排行