tomcat4默认连接器简要分析

1、HTTP 1.1新特性

1)持久连接:connection: keep-alive

2)块编码:使用长连接后,发送方大部分时候无法计算出要发送的内容长度,也不能等所有资源都准备好了再发送,HTTP 1.1 使用transfer-encoding header来处理这个问题。transfer-encoding表示有多少以块形式的字节流将会被发送给接收方,对于每一个块数据,长度(16进制)+ CR/LF + 数据将会被发送,整个事物以0\r\n结束。例如:I'm as helpless as a kitten up a tree.这个数据如果以3个块发送的话,其发送格式为:

1D\r\n
I'm as helpless as a kitten u
9\r\n
p a tree.
0\r\n

3) Expect: 100-continue Header 使用,当要发送长内容请求的时候,客户端在无法保证服务器一定会接收的时候,可以先发送这个header到服务器询问是否允许客户端的长内容请求,如果允许,服务端会返回HTTP/1.1 100 Continue + CRLF + CRLF给客户端。

2、连接器的职责:

1)接收客户端的请求,解析HTTP协议:请求行(GET /api/xxx HTTP/1.1 ...)解析,header解析,数据解析
2)构建Request对象
3)构建Response对象

3、连接器处理流程

image.png
image.png
image.png

整个类图分为三块,类图1为连接器和容器、连接器和处理器之间的关系;类图2为Request结构图,类图3为Response结构图。

3、1 HttpConnector启动阶段

1)通过调用initialize()方法和open()方法,创建了一个ServerSocket对象

2)调用start()方法,一是启动接收客户端请求的线程,二是初始化HttpProcessor对象池,该对象池为Stack,这里主要看一下HttpProcessor的创建和启动。

private HttpProcessor newProcessor() {
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        if (processor instanceof Lifecycle) {
            try {
                /**
                 * HttpProcessor 本身是一个Runnable和Lifecycle,在这里对其进行了启动操作,
                 * 其实就是启动了一个线程,来执行HttpProcessor这个Runnable
                 */
                ((Lifecycle) processor).start();
            } catch (LifecycleException e) {
                log("newProcessor", e);
                return (null);
            }
        }
        created.addElement(processor);
        return (processor);
    }
3、2 HttpConnector对请求处理过程
public void run() {
    socket = serverSocket.accept();
    HttpProcessor processor = createProcessor();
    processor.assign(socket);
}

run()主体逻辑就上面三行代码,在接收到请求后,先通过createProcessor方法获取一个HttpProcessor对象,createProcessor获取HttpProcessor对象的逻辑比较简单,直接看代码就行。

private HttpProcessor createProcessor() {
        synchronized (processors) {
            if (processors.size() > 0) {
// 对象池中还有对象,直接从池子里面获取
                return ((HttpProcessor) processors.pop());
            }
            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
// 池子里面没有了,只要没有超过设置的最大容量,就创建一个对象
                return (newProcessor());
            } else {
                if (maxProcessors < 0) {
// 如果最大容量设置为小于0,则直接创建一个对象
                    return (newProcessor());
                } else {
                    return (null);
                }
            }
        }
    }

获取到HttpProcessor之后,调用其assign方法。

3、3 HttpProcessor启动过程

通过上面知道,HttpProcessor是在HttpConnector的newProcessor()中进行创建和启动的,在start()中,其主要工作是启动了处理线程

public void start() throws LifecycleException {
        if (started)
            throw new LifecycleException
                (sm.getString("httpProcessor.alreadyStarted"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;
// 启动线程,Runnable为本身
        threadStart();
    }

现在看一下这个线程的主要工作是啥:

 public void run() {
        // Process requests until we receive a shutdown signal
        while (!stopped) {
            // Wait for the next socket to be assigned
            // 1、调用await()方法获取一个socket
            Socket socket = await();
            if (socket == null)
                continue;
            // Process the request from this socket
            try {
            // 2、对socket进行处理
                process(socket);
            } catch (Throwable t) {
                log("process.invoke", t);
            }
            // Finish up this request
            // 3、对象回收
            connector.recycle(this);
        }
        // Tell threadStop() we have shut ourselves down successfully
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    }

可以看到,其工作主要是:从await()方法获取socket,交给process()方法处理,然后HttpConnector对该对象进行回收再利用。

看一下socket是如何获取的:

private synchronized Socket await() {
        // Wait for the Connector to provide a new Socket
        while (!available) {
            try {
                /**
                 * 进入等待状态,直到HTTPConnector线程调用了notifyAll().
                 * 这个唤醒操作在assign()中完成。
                 */
                wait();//
            } catch (InterruptedException e) {
            }
        }
        // Notify the Connector that we have received this Socket
        Socket socket = this.socket;
        available = false;
        /**
         * 这里是唤醒assign()中的wait(),让其可以继工作了。
         */
        notifyAll();
        return socket;
    }

await首先进入一个等待状态,只有被其他线程唤醒了,才会进行下一步的工作,也就是返回socket,那么这个等待由谁唤醒呢?在HTTPConnector的run()方法中可以知道,socket来源于HTTPConnector,然后其把这个socket传给了HttpProcessor的assign()方法:

synchronized void assign(Socket socket) {
        // Wait for the Processor to get the previous Socket
        while (available) {
            try {
// 让当前线程进入等待状态,assign方法由HTTPConnector所在的线程
// 调用,wait是在HttpProcessor对象中起调的,所以这里是
// HttpProcessor让HTTPConnector所在的线程进行等待
                wait();
            } catch (InterruptedException e) {
            }
        }
        // Store the newly available Socket and notify our thread
        this.socket = socket;
        available = true;
// 唤醒其他线程中的所有wait()方法,这里的调用者是HttpProcessor,
//也就是唤醒HttpProcessor线程中所有的wait()方法
        notifyAll();
    }

assign()方法接收来自于HTTPConnector的socket,然后赋值在HttpProcessor的全局变量this.socket上,在await()方法中就能获取到这个socket了,因为assign()工作在HTTPConnector所属的线程,而await()工作在HttpProcessor所属的线程中,两者之间通过wait()和notifyAll()来互相通信。

这里有个疑问,不知道assign()里面为什么要wait(),await()里面为什么要notifyAll(),根据设计,HttpProcessor在当前socket没有处理完的时候,其不会被回收到对象池中,也就根本没有机会去处理下一个socket,但是这里却这样设计了,而且看await方法返回的socket也不是全局变量,而是用了一个局部变量来存储然后返回的是这个局部变量。官方的解释是说全局的变量用来放置下一个到来的socket,以防当前socket在没有完全处理完成而下一个socket又到来的情况,但是这种情况是怎么出现的,不解。

3、4 HttpProcessor解析HTTP协议过程简析

tomcat默认连接器对socket的处理逻辑,主要在HttpProcessor类中的process()方法,其主要工作是解析连接,解析请求行,解析头部,这里它没有对参数进行解析,参数是在需要的时候才会进行解析,主要是不过多的占用CPU时间,使CPU有更多的时间来处理客户的请求,提升并发量。

private void process(Socket socket) {
        // 用来记录处理过程是否正确
        boolean ok = true;
        //  用来标记Response接口中的 finishResponse 方法是否应该被调用
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;// 输出流
        // Construct and initialize the objects we will need
        try {
            // 包装了一个InputStream,用来解析请求行和头部信息
            input = new SocketInputStream(socket.getInputStream(),
                                          connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }

        // 是否持久连接,HTTP/1.1才设置为true,见parseRequest()
        keepAlive = true;
        while (!stopped && ok && keepAlive) {
            finishResponse = true;
            try {// request/response基本配置,输入输出流
                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
                if (ok) {
                    // 解析服务器地址和端口号
                    parseConnection(socket);
                    // 这个主要是用来解析请求行: GET /api/xxx?a=11&b=22.... HTTP/1.1
                    parseRequest(input, output);
                    // Header解析
                    if (!request.getRequest().getProtocol().startsWith("HTTP/0")) {
                        parseHeaders(input);
                    }
                    // 如果是HTTP/1.1,回复"HTTP/1.1 100 Continue\r\n\r\n"
                    if (http11) {
                        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 {
                if (ok) {
                    // 交给Container来处理请求
                    connector.getContainer().invoke(request, response);
                }
            } 

            // Finish up the handling of the request
            if (finishResponse) {
                try {
                    response.finishResponse();
                } 
                try {
                    request.finishRequest();
                } 
                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();
        }
        socket = null;
    }

整体流程上还是比较清晰的,具体怎么解析的这里就不描述了,在处理完成后,是交给了容器的invoke方法进行处理的,至于容器是如何处理这个请求的,这个只有在分析完容器后才知晓了。

你可能感兴趣的:(tomcat4默认连接器简要分析)