Tomcat源码解析四(连接器启动过程解析)

一、逻辑分析:

连接器中有三个核心组件:EndpointProcessor Adapter, 其中Endpoint Processor 放在一起抽象成了 ProtocolHandler 组件, 关系图如下:

  • Endpoint 

Endpoint 是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此 Endpoint 是用来实现 TCP/IP 协议的。

Endpoint 是一个接口,对应的抽象实现类是 AbstractEndpoint,而 AbstractEndpoint 的具体子类,比如在 NioEndpoint Nio2Endpoint 中,有两个重要的子组件:Acceptor SocketProcessor

其中 Acceptor 用于监听 Socket 连接请求SocketProcessor 用于处理接收到的 Socket 请求,它实现 Runnable 接口,在 run 方法里调用协议处理组件 Processor 进行处理。为了提高处理能力,SocketProcessor 被提交到线程池来执行。而这个线程池叫作执行器(Executor),我在后面的专栏会详细介绍 Tomcat 如何扩展原生的 Java 线程池。

  • Processor

如果说 Endpoint 是用来实现 TCP/IP 协议的,那么 Processor 用来实现 HTTP 协议,Processor 接收来自 Endpoint Socket,读取字节流解析成 Tomcat Request Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象。

Processor 是一个接口,定义了请求的处理等方法。它的抽象实现类 AbstractProcessor 对一些协议共有的属性进行封装,没有对方法进行实现。具体的实现有 AjpProcessorHttp11Processor 等,这些具体实现类实现了特定协议的解析方法和请求处理方式。

  • Adapter  

由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat 定义了自己的 Request 类来存放这些请求信息。ProtocolHandler 接口负责解析请求并生成 Tomcat Request 。但是这个 Request 对象不是标准的 ServletRequest,也就意味着,不能用 Tomcat Request 作为参数来调用容器。Tomcat 设计者的解决方案是引入 CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方法

 连接器的组件图: 

Endpoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor run 方法会调用 Processor 组件去解析应用层协议,Processor 通过解析生成 Request 对象后,会调用 Adapter Service 方法。

 

二、源码分析:

Connector#startInternal()方法实现 

    @Override
    protected void startInternal() throws LifecycleException {

        //端口验证
        if (getPort() < 0) {
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
        }

        setState(LifecycleState.STARTING);

        try {
            /**
             * 启动ProtocolHandler, Connector使用ProtocolHandler来处理请求,不同的ProtocolHandler代表不同的连接类型
             *
             * protocolHandler ==> {@link org.apache.coyote.http11.Http11NioProtocol}
             *
             * {@link AbstractProtocol#start()}
             */
            protocolHandler.start();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

连接器中Endpoint Processor 放在一起抽象成了 ProtocolHandler 组件, 因此此处调用AbstractProtocol#start()启动连接器 

由于 I/O 模型和应用层协议可以自由组合,比如 NIO + HTTP 或者 NIO.2 + AJPTomcat 的设计者将网络通信和应用层协议解析放在一起考虑,设计了一个叫 ProtocolHandler 的接口来封装这两种变化点各种协议和通信模型的组合有相应的具体实现类。比如:Http11NioProtocol AjpNioProtocol除了这些变化点,系统也存在一些相对稳定的部分,因此 Tomcat 设计了一系列抽象基类来封装这些稳定的部分,抽象基类 AbstractProtocol 实现了 ProtocolHandler 接口每一种应用层协议有自己的抽象基类,比如 AbstractAjpProtocol 和 AbstractHttp11Protocol,具体协议的实现类扩展了协议层抽象基类。继承关系如下:


AbstractProtocol#start()方法实现:

    @Override
    public void start() throws Exception {
        if (getLog().isInfoEnabled()) {
            getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
        }

        /**
         * {@link AbstractEndpoint#start()}
         */
        endpoint.start();

        //启动超时线程, 用来处理请求超时的连接
        asyncTimeout = new AsyncTimeout();
        Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
        int priority = endpoint.getThreadPriority();
        if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
            priority = Thread.NORM_PRIORITY;
        }
        timeoutThread.setPriority(priority);
        timeoutThread.setDaemon(true);
        /**
         * {@link AsyncTimeout#run()}
         */
        timeoutThread.start();
    }

分析:

  1. 启动Endpoint
  2. 启动超时线程, 用来处理请求超时的连接

AbstractEndpoint#start()方法实现:

public final void start() throws Exception {
        if (bindState == BindState.UNBOUND) {  //在初始化init()方法中已经绑定端口, 此处不再执行
            bind();
            bindState = BindState.BOUND_ON_START;
        }
        /**
         * {@link NioEndpoint#startInternal()}
         */
        startInternal();
    }

分析:

  1. 如果没有绑定端口, 则调用bind()方法绑定 (在初始化Endpoint时会绑定地址端口)
  2. 调用AbstractEndpoint的实现类NioEndpoint的startIniternal()方法进行初始化操作

NioEndpoint#startInternal()方法实现

    @Override
    public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;

            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getProcessorCache());
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getEventCache());
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                    socketProperties.getBufferPool());

            /**
             * 创建线程池
             * Endpoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,
             * SocketProcessor 的 run 方法会调用 Processor 组件去解析应用层协议
             */
            if ( getExecutor() == null ) {
                createExecutor();
            }
            /**
             * 初始化连接限制
             * NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒绝
             */
            initializeConnectionLatch();

            //创建Poller线程组, 并遍历启动
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                /**
                 * Acceptor接收到请求后会交给Poller处理, 而poller的处理逻辑在run方法中
                 * {@link Poller#run()}
                 */
                pollerThread.start();
            }

            /**
             * 启动Acceptor线程, 开始接收客户端请求
             */
            startAcceptorThreads();
        }
    }

分析:

  1. 初始化同步栈;  processorCache: 缓存SocketProcessor实例; eventCache: 缓存poller事件; nioChannels: 字节缓冲区高速缓存,每个通道保存一组缓冲区(两个,SSL除外,四个)
  2. 创建线程池;  Endpoint 接收到 Socket 连接后,生成一个 SocketProcessor 任务提交到线程池去处理,SocketProcessor 的 run 方法会调用 Processor 组件去解析应用层协议
  3. 初始化连接限制;  NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒绝
  4. 创建Poller线程组, 并遍历启动
  5. 启动Acceptor线程, 开始接收客户端请求

(1) 创建线程池: AbstractEndpoint#createExecutor()方法实现:

public void createExecutor() {
        internalExecutor = true;
        //创建任务队列
        TaskQueue taskqueue = new TaskQueue();
        //创建任务线程工厂
        TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
        //创建线程池
        executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
        taskqueue.setParent( (ThreadPoolExecutor) executor);
    }

(2) 初始化连接限制: AbstractEndpoint#initializeConnectionLatch()方法实现:

protected LimitLatch initializeConnectionLatch() {
        if (maxConnections==-1) return null;
        if (connectionLimitLatch==null) {
            connectionLimitLatch = new LimitLatch(getMaxConnections());
        }
        return connectionLimitLatch;
    }

(3) 创建Poller线程组, 并遍历启动: poller线程run方法逻辑实现:

       /**
         * tomcat的后台poller线程的主逻辑 , 循环处理以下几件事情 :
         * 1. 每次循环处理PollerEvent事件队列中所有的事件
         * 2. 每次循环处理NIO selector所关注的事件中发生的事件(所有请求的处理,实际上这里都委托给了worker线程)
         * 3. 超时处理:每次循环中特定条件满足时执行一次超时处理
         * 4. 结束检测:如果被通知结束,执行结束逻辑,也就是该run()方法内的while-loop的结束
         */
        @Override
        public void run() {
            //轮询检测是否有socket可读
            while (true) {
                // 没有收到停止消息,处理PollerEvent事件队列中所有的事件
                boolean hasEvents = false;

                try {
                    /**
                     * 没有收到停止消息,处理PollerEvent事件队列中所有的事件
                     */
                    if (!close) {
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            //if we are here, means we have other stuff to do
                            //do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(selectorTimeout);
                        }
                        wakeupCounter.set(0);
                    }
                    /**
                     * 收到结束通知,poller线程停止前先处理掉PollerEvent队列中的事件
                     */
                    if (close) {
                        events();
                        // poller关闭前的超时处理
                        timeout(0, false);
                        // 结束Java NIO selector,也就是关闭接收和处理服务
                        try {
                            selector.close();
                        } catch (IOException ioe) {
                            log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                        }
                        // 被通知结束并且处理完收尾工作,现在结束整个线程的while-loop
                        break;
                    }
                } catch (Throwable x) {
                    // 出现异常不退出,记日志然后 poller 线程 while-loop继续执行
                    ExceptionUtils.handleThrowable(x);
                    log.error("",x);
                    continue;
                }
                //either we timed out or we woke up, process events first
                if ( keyCount == 0 ) hasEvents = (hasEvents | events());

                Iterator<SelectionKey> iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;

                /**
                 * 遍历处理所有待处理的NIO事件
                 */
                while (iterator != null && iterator.hasNext()) {
                    SelectionKey sk = iterator.next();
                    NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (attachment == null) {
                        iterator.remove();
                    } else {
                        iterator.remove();
                        /**
                         * 处理有待处理事件的SelectionKey, 其实真正的处理都委托给了 worker 线程
                         */
                        processKey(sk, attachment);
                    }
                }//while

                //process timeouts,正常运行中处理超时
                timeout(keyCount,hasEvents);
            }//while

            getStopLatch().countDown();
        }

(4) 启动Acceptor线程, 开始接收客户端请求: AbstractEndpoint#startAcceptorThreads()方法实现

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());
            /**
             * 调用start方法后, 将会执行线程的run()方法
             * {@link NioEndpoint.Acceptor#run()}
             */
            t.start();
        }
    }

acceptor线程执行逻辑:

        @Override
        public void run() {

            int errorDelay = 0;

            //循环处理接受请求, 直到收到关闭命令
            while (running) {

                //如果endpoint是暂停状态的, 进入循环
                while (paused && running) {
                    state = AcceptorState.PAUSED;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                state = AcceptorState.RUNNING;

                try {
                    /**
                     * LimitLatch:连接控制器,负责控制最大连接数
                     * NIO 模式下默认是 10000,达到这个阈值后,连接请求被拒绝。
                     */
                    countUpOrAwaitConnection(); // 父类方法

                    SocketChannel socket = null;
                    try {
                        /**
                         * 获取到客户端发送过来的连接
                         */
                        socket = serverSock.accept();
                    } catch (IOException ioe) {
                        //如果出现异常, 没有获取到socket, 则将connectionLimitLatch减一
                        countDownConnection();
                        if (running) {
                            //必要时引入延迟
                            errorDelay = handleExceptionWithDelay(errorDelay);
                            throw ioe;
                        } else {
                            break;
                        }
                    }
                    //成功接受,重置错误延迟
                    errorDelay = 0;

                    // Configure the socket
                    if (running && !paused) {
                        /**
                         * 此处虽然获取到了Socket, 但是用户线程需要等待内核把数据从网卡传输到内核, 再由用户线程从内核的缓冲区获取到相关数据,
                         * 在此过程中,需要把socket包装为PollerEvent注册到Poller线程中, Poller不断的通过内部的 Selector 对象向内核查询 Channel 的状态,
                         * 一旦可读就生成任务类 SocketProcessor 交给Executor去处理
                         *
                         * 将把socket进行包装移交给合适的处理器
                         * 如果出现异常, 则返回false,关闭socket
                         */
                        if (!setSocketOptions(socket)) {
                            closeSocket(socket);
                        }
                    } else {
                        closeSocket(socket);
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
            state = AcceptorState.ENDED;
        }

至此, 连接器已启动完成;

相关文章:

      Tomcat源码解析一(Tomcat整体架构解析)

      Tomcat源码解析二(Tomcat初始化过程解析)

      Tomcat源码解析三(Tomcat启动过程解析)

      Tomcat源码解析四(连接器启动过程解析)

      Tomcat源码解析五(容器的启动过程解析)

      Tomcat源码解析六(Http请求过程解析_基于NIO)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值