• tomcat源码分析(三)一次http请求的旅行-从Socket说起


    tomcat源码分析()一次http请求的旅行

    http请求旅行之前,我们先来准备下我们所需要的工具。首先要说的就是Connector,其作为Service的子容器,承担着http请求的核心功能。那我们先来准备下一啊吧。

    我们知道一次网络请求过来之后,从网络的角度来看,是经过物理层→链路层→网络层->传输层->应用层,如下图所示。

                

     

     

      我们所熟知的的Socket处于TCP(传输层),操作系统为我们提供来一套API来操作Socket,而tomcat其任务就是针对传输层过来的Socket进行包装,并实现应用层的协议,最常见的应用层协议应该算是http协议了。接下来就来具体看看tomcat是如何实现http协议(实际上tomcat还实现了ajp协议以及处理请求的。

    我们这里以最常见的BIO(阻塞试IO)的方式来分析。我们先来看看tomcat是怎么处理TCP连接的。在org.apche.tomcat.util.net包主要是用于处理网络请求的,即对TCP的处理。

      首先我们来看一下org.apache.tomcat.util.net.AbstractEndPoint这个类。在Tomcat的对请求的设计当中,由专门的线程接受TCP连接,并直接将TCP连接转交给工作线程。在AbstrctEndPoint中有一个抽象的静态内部类我们来一起看一下。

    public abstract static class Acceptor implements Runnable {
            public enum AcceptorState {
                NEW, RUNNING, PAUSED, ENDED
            }
    
            protected volatile AcceptorState state = AcceptorState.NEW;
            public final AcceptorState getState() {
                return state;
            }
    
            private String threadName;
            protected final void setThreadName(final String threadName) {
                this.threadName = threadName;
            }
            protected final String getThreadName() {
                return threadName;
            }
        }

      可以看出在这个静态内部类中并没有实现run()方法,其实现交给子类来实现。在Tomcat中实际定义来一个 Acceptor数组来表示一组接受TCP连接的线程。我们在简单看一下其启动这个接受线程的代码实现。

     

    protected final void startAcceptorThreads() {
            int count = getAcceptorThreadCount();
            acceptors = new Acceptor[count];
    
            for (int i = 0; i < count; i++) {
                acceptors[i] = createAcceptor();
                String threadName = getName() + "-Acceptor-" + i;
                acceptors[i].setThreadName(threadName);
                Thread t = new Thread(acceptors[i], threadName);
                t.setPriority(getAcceptorThreadPriority());
                t.setDaemon(getDaemon());
                t.start();
            }
        }

     

      其中count这个我们是可以在server.xml中去配置的,一般情况下,会配置1-2。也就是说接受TCP连接的线程也只是1-2个。

      说到这里,我们也应该来说重点来,就是接受线程是如何具体工作的,我们来看JIOEndPoint,这个类是AbstractEndPoint的子类,也是设计来处理TCP连接的,这个类实现了一个简单的服务器,会有一到2个监听线程来监听Socket,对于每一个TCP连接,都会从创建一个工作线程来处理。

    刚刚我们说道AbstractEndPoint中的抽象静态内部类Acceptor,在其子类JIOEndPoint中也存在一个内部类,继承自Acceptor,并实现来run();方法。我们来看一下。

     

     

    protected class Acceptor extends AbstractEndpoint.Acceptor {
    
            @Override
            public void run() {
    
                int errorDelay = 0;
    
                // Loop until we receive a shutdown command
                while (running) {
    
                    // Loop if endpoint is paused
                    while (paused && running) {
                        state = AcceptorState.PAUSED;
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
    
                    if (!running) {
                        break;
                    }
                    state = AcceptorState.RUNNING;
    
                    try {
                        //if we have reached max connections, wait
                        countUpOrAwaitConnection();
    
                        Socket socket = null;
                        try {
                            // Accept the next incoming connection from the server
                            // socket
                            socket = serverSocketFactory.acceptSocket(serverSocket);
                        } catch (IOException ioe) {
                            countDownConnection();
                            // Introduce delay if necessary
                            errorDelay = handleExceptionWithDelay(errorDelay);
                            // re-throw
                            throw ioe;
                        }
                        // Successful accept, reset the error delay
                        errorDelay = 0;
    
                        // Configure the socket
                        if (running && !paused && setSocketOptions(socket)) {
                            // Hand this socket off to an appropriate processor
                            if (!processSocket(socket)) {
                                countDownConnection();
                                // Close socket right away
                                closeSocket(socket);
                            }
                        } else {
                            countDownConnection();
                            // Close socket right away
                            closeSocket(socket);
                        }
                    } catch (IOException x) {
                        if (running) {
                            log.error(sm.getString("endpoint.accept.fail"), x);
                        }
                    } catch (NullPointerException npe) {
                        if (running) {
                            log.error(sm.getString("endpoint.accept.fail"), npe);
                        }
                    } catch (Throwable t) {
                        ExceptionUtils.handleThrowable(t);
                        log.error(sm.getString("endpoint.accept.fail"), t);
                    }
                }
                state = AcceptorState.ENDED;
            }
        }

     

      其核心在接收到TCP连接之后,即在接收到Socket,会调用processSocket(Socket socket);这个方法。我们再来关注一下这个方法。

     

    protected boolean processSocket(Socket socket) {
            // Process the request from this socket
            try {
                SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
                wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
                wrapper.setSecure(isSSLEnabled());
                // During shutdown, executor may be null - avoid NPE
                if (!running) {
                    return false;
                }
                getExecutor().execute(new SocketProcessor(wrapper));
            } catch (RejectedExecutionException x) {
                log.warn("Socket processing request was rejected for:"+socket,x);
                return false;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                // This means we got an OOM or similar creating a thread, or that
                // the pool and its queue are full
                log.error(sm.getString("endpoint.process.fail"), t);
                return false;
            }
            return true;
        }

     

      其核心代码在于 getExecutor().execute(new SocketProcessor(wrapper));getExecutor()会返回Executor对象(AbstractEndPointcreateExecutor()建立了线程池),由线程池中的线程来处理该Socket。我们再来看一下SocketProccessor这个在JIOEndPoint中的内部类,这个类(注意此时已经在工作线程之中)中核心代码

                if ((state != SocketState.CLOSED)) {
                            if (status == null) {
                                state = handler.process(socket, SocketStatus.OPEN_READ);
                            } else {
                                state = handler.process(socket,status);
                            }
                        }

       从中我们可以看到实际处理又交给来Handler来处理,那么Handler怎么处理的,我们会在下一节当中具体阐述。这一节就先讲述到这里,下一节会讲述handler具体处理过程。

  • 相关阅读:
    在指定文件夹目录下打开jupyter notebook
    防止sql注入
    惰性函数——适合外层函数只需要执行一次
    Text类型
    怎样理解阻塞非阻塞与同步异步的区别?
    Element类型
    避免使用eval()
    javascript 连等赋值问题
    类数组转化为数组
    DOM10-1节点层次
  • 原文地址:https://www.cnblogs.com/yanfengfree/p/6128737.html
Copyright © 2020-2023  润新知