2、連接器Connector

這一篇章,我想介紹一下tomcat容器中的連接器部分,並將tomcat4、tomcat6及tomcat8他們的連接器對比一下,看看做了哪些改進。

1、首先我們再回顧一下模擬tomcat容器這個應用中連接器的代碼

public class Connector implements Runnable {

    Container container;

    public Container getContainer() {
        return container;
    }

    public void setContainer(Container container) {
        this.container = container;
    }

    @Override
    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(8080,
                    20,
                    InetAddress.getByName("localhost"));

            while (true) {
                Socket socket = serverSocket.accept();
                Request request = new ServletRequest();
                request.setInputStream(socket.getInputStream());
                Response response = new ServletResponse();
                response.setOutputStream(socket.getOutputStream());

                ((ServletRequest) request).parseRequest(request);

                container.invoke(request, response);
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void start() {
        Thread t = new Thread(this);
        t.start();
    }
}

連接器的全部工作也就是上面這塊代碼完成的功能,創建

1、服務端套接字

2、監聽端口

3、接收客戶端連接

4、解析報文

5、調用容器

2、下面我們看看tomcat4連接器怎麼處理的

我繼續會處理掉一些此階段不太關注的代碼

public void initialize()
    throws LifecycleException {
        // 這個字段是防止該對象重複初始化
        if (initialized)
            throw new LifecycleException (
                sm.getString("httpConnector.alreadyInitialized"));

        this.initialized=true;

        // Establish a server socket on the specified port
        
       serverSocket = open();
}

// open創建一個ServerSocket
private ServerSocket open()
    throws IOException, KeyStoreException, NoSuchAlgorithmException,
           CertificateException, UnrecoverableKeyException,
           KeyManagementException
    {

        // 獲取ServerSocket工廠
        ServerSocketFactory factory = getFactory();

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

可見初始化操作創建了一個ServerSocket套接字

@Override
    public void start() throws LifecycleException {

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

        // 創建一個後臺線程,connector是一個Runnable實現類,這個函數就是啓動了線程
        threadStart();

        // 此處創建了一個Processor對象池,每一個Processor是一個線程,newProcessor的時候順便也啓動了線程,所以我們接下來分別看看兩個線程做的什麼
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            HttpProcessor processor = newProcessor();
            recycle(processor);
        }

    }

我們來看看HttpConnector類定義,可以發現其實現了一個Runnable接口,threadStart這個函數就是啓動了HttpConnector的線程

tomcat4這個時候jdk還沒有實現線程池,這裏是通過棧實現了一個對象池,對象池裏存放的是Processor,newProcessor的時候,順便啓動了線程,接下來我們看看HttpConnector的run和HttpProcessor的run

HttpConnector

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

            // 分配一個空閒的HttpProcessor
            HttpProcessor processor = createProcessor();
           // 給processor分配工作
            processor.assign(socket);


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

HttpProcessor

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

    }

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

        return (socket);

    }

 connector接收到一個新的連接,從對象池中獲取一個Processor對象,並將socket分配給對象,而Processor,如果沒有新的要處理的業務,會阻塞在await等待一個新的socket,這裏是典型的等待通知的應用。

Connector接收到新的請求,assign(Socket)將新請求的socket分配給processor,這裏用到了synchronized同步處理,且synchronized保證了裏面內存區域數據在connector線程與processor之間的可見性。將socket傳給了processor之後,將available賦值爲true,並通知,這時候processor取消阻塞,獲取到新的socket,並調用process。

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

                }
            } 

            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    connector.getContainer().invoke(request, response);
                }
            } 

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

           
            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;

    }

process()就是接收數據,解析http請求,調用容器,這裏沒有做什麼特殊設計。

所以我這裏推薦大家讀源碼的話,版本選擇tomcat4,這個版本沒有過度設計,對於通過讀源碼深入瞭解tomcat再合適不過。

3、我們再來看看tomcat6的連接器

這裏就不講解代碼了,可以按照這個思路去看下tomcat6的源碼,我這裏講解一下tomcat6連接器的變化部分與目的。

這裏針對連接的創建與監聽,報文的處理及調用容器均做了重構,添加了EndPoint、Processor、Adapter三個模塊。

EndPoint:負責與客戶端的對接,在系統的設計中,這部分是變化的,tomcat6就有加入NIO非阻塞 I/O 多路複用,tomcat8中還加入了異步IO,

ProtocolHandler:負責協議的解析,Http11Protocol負責的是http協議,

Adapter:通過適配器調用容器,適應不同協議下輸出到容器的數據格式是一致的。

上面三個模塊的重構封裝符合設計中的開閉原則(開放擴展,關閉修改)

後期我會針對tomcat6的連接器做一次詳細的描述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章