zoukankan      html  css  js  c++  java
  • Tomcat之NIO 启动与应用分析

    概述

        从入门Web开始一直在使用Tomcat,随着对网络相关的知识的进一步了解,觉得越有必有去阅读一下常用的开源服务器的整个工作流程,以及使用场景,对比几款服务器的优劣势、最终根据合适的业务场景进行优化。于是有了这一篇启动相关的源码分析,使用到的 Tomcat版本为  9.0.6 ,技术有限,难免出现错误,欢迎指出,也期待各路大神指点。

      首先启动Tomcat方式这里采取编程的方式,maven引入坐标

            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-core</artifactId>
                <version>9.0.6</version>
            </dependency>

    1.启动方法

            Tomcat tomcat = new Tomcat();
            Connector connector = new Connector();
            connector.setPort(8080);
            tomcat.setConnector(connector);
            tomcat.start();
            tomcat.getServer().await();
    1. Connector 的构造方法默认会使用Http11NioProtocol协议(NIO)来对客户端连接进行处理,通过反射获得其对象,赋值给 protocolHandler
    2. tomcat.setConnector()会初始化一个 StandardServer和StandardService,并且将StandardServicet添加到StandardServer里,Connector添加 StandardService里,因此这里三者之间的关系就是
    3. Tomcat.start() 则利用初始化的StandardServer,调用其start()方法
    4. Http11Nioprotocol 协议是用来处理Nio的协议,在它的构造方法里,实例化了NioEnpoint 交给 AbstractProtocol 保管,然而真正实现Nio建立网络连接则是通过该类完成。后面细说是如何到NioEnponit的

      Connector默认使用Nio协议

    public Connector() {
            this("org.apache.coyote.http11.Http11NioProtocol");
        }
    
    
        public Connector(String protocol) {
            boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
                    AprLifecycleListener.getUseAprConnector();
    
            if ("HTTP/1.1".equals(protocol) || protocol == null) {
                if (aprConnector) {
                    protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
                } else {
                    protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
                }
            } else if ("AJP/1.3".equals(protocol)) {
                if (aprConnector) {
                    protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
                } else {
                    protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
                }
            } else {
                protocolHandlerClassName = protocol;
            }
    
            // Instantiate protocol handler
            ProtocolHandler p = null;
            try {
                Class<?> clazz = Class.forName(protocolHandlerClassName);
                //反射获得对象
                p = (ProtocolHandler) clazz.getConstructor().newInstance();
            } catch (Exception e) {
                log.error(sm.getString(
                        "coyoteConnector.protocolHandlerInstantiationFailed"), e);
            } finally {
            }
                this.protocolHandler = p;
            }


    2.LifecycleBase.start()  初始化与启动

       

    @Override
        public final synchronized void start() throws LifecycleException {
            //state默认是 LifecycleState.NEW,首次执行时,先通过init()方法实现初始化
            if (state.equals(LifecycleState.NEW)) {
                init();
            }
            try {
                setStateInternal(LifecycleState.STARTING_PREP, null, false);
                //初始化之后,开始启动tomcat服务器
                startInternal();
         ........... }

      

       

    1. LifecycleBase 是 StandardServer、Connector、StandardService的父类,当StandardServer的 start() 方法被调用时,将会调用父类LifecycleBase的 start() 方法,同样的,对Connector、StandardService来说,调用 start() 方法也会调用父类的LifecycleBase的start()方法。

    2. LifecycleBase 中持有 用 volatile 关键字 修饰的state 字段,该字段用来标识 Tomcat 启动时所处的状态,默认是 LifecycleState.NEW,因此进入init() 方法 
    3. 每个LifecycleState枚举类一 一对应着Tomcat 服务器的状态,每个枚举类状态又有对应的Tomcat启动的事件,可以通过实现  LifecycleListener 接口进行自定义。在Tomcat启动时会被放进 lifecycleListeners 中
    public enum LifecycleState {
        NEW(false, null),
        INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
        INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
        STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
        STARTING(true, Lifecycle.START_EVENT),
        STARTED(true, Lifecycle.AFTER_START_EVENT),
        STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
        STOPPING(false, Lifecycle.STOP_EVENT),
        STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
        DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
        DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
        FAILED(false, null);
    
        private final boolean available;
        private final String lifecycleEvent;

     3.LifecycleBase.init()  初始化StandardServer 

    public final synchronized void init() throws LifecycleException {
            if (!this.state.equals(LifecycleState.NEW)) {
                this.invalidTransition("before_init");
            }
    
            try {
                //从lifecycleListeners 获取 before_init事件进行调用
                this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);
                this.initInternal();
                //对应 after_init 事件
                this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);
            } catch (Throwable var2) {
                this.handleSubClassException(var2, "lifecycleBase.initFail", this.toString());
            }
        }
    1. init() 方法是通过调用子类实现的initInternal() 方法来进行抽象的扩展,相当于一个没有具体实现的抽象函数,交给子类去初始化处理(这里是StandardServer 的调用过程)
    2. 在初始化方法调用的前后,使用了setStateInternal() 方法进行捕捉,这里便是对 实现了 LifecycleListener 接口的监听器做了实现,在其他地方,如:start、stop 都有进行 listener 的调用

    4.StandardServer.initInternal() 初始化 server 

      

       @Override
        protected void initInternal() throws LifecycleException {
            super.initInternal();
            // ...... 暂时不关心中间代码
            // Initialize our defined Services
            for (int i = 0; i < services.length; i++) {
                //这里调用StandardService的init()方法
                services[i].init();
            }
        }
    1. 步骤3 处理逻辑大致相同,init()方法里 是通过调用子类实现的 initInternal() 方法来初始化,同样的捕捉初始化的前后事件  before_init、after_init 

    5. StandardService.initInternal()  初始化service

    @Override
        protected void initInternal() throws LifecycleException {
            super.initInternal();
            //省略若干......
            // Initialize our defined Connectors
            synchronized (connectorsLock) {
                for (Connector connector : connectors) {
                    //此处调用connector进行初始化
                    connector.init();
                }
            }
        }
    1.  此处同理,调用 connector.init(),通过父类调用子类实现的 钩子函数 initInternal() 来初始化Connector

    6.Connector.initInternal() 初始化connector

      

    @Override
        protected void initInternal() throws LifecycleException {
    
            super.initInternal();
            //省略若干..............
            try {
                //初始化 Http11NioProtocol协议
                protocolHandler.init();
            } catch (Exception e) {
                throw new LifecycleException(
                        sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
            }
        }
    1. protocolHandler是我们在实例化 Connector 时,使用 默认的Http11NioProtocol协议。
    2. 其init()方法,则在父类 AbstractProtocol 中实现

    6.AbstractProtocol.init()  初始化协议

      

        @Override
        public void init() throws Exception {
            if (getLog().isInfoEnabled()) {
                getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
            }
    
            //省略若干行...
    
            String endpointName = getName();
            endpoint.setName(endpointName.substring(1, endpointName.length()-1));
            endpoint.setDomain(domain);
            //初始化NioEndpoint
            endpoint.init();
        }
    1. NioEndpoint 的init() 方法,在其父类AbstractEndpoint中实现,用来把公共的抽象到父类中实现(如注册JMX),
    2. 然后再提供给子类bind() 方法,通过实现该方法来完成一次函数的回调
    public final void init() throws Exception {
            if (bindOnInit) {
                //交给子类实现
                bind();
                bindState = BindState.BOUND_ON_INIT;
            }
            if (this.domain != null) {
                // Register endpoint (as ThreadPool - historical name)
                oname = new ObjectName(domain + ":type=ThreadPool,name="" + getName() + """);
                Registry.getRegistry(null, null).registerComponent(this, oname, null);
    
                for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
                    registerJmx(sslHostConfig);
                }
            }
        }

    7. NioEndpoint.bind() 

    @Override
        public void bind() throws Exception {
            //初始化ServerSocker
            initServerSocket();
    
            // Initialize thread count defaults for acceptor, poller
            //如果acceptor线程个数小于0,则默认为1
            if (acceptorThreadCount == 0) {
                // FIXME: Doesn't seem to work that well with multiple accept threads
                acceptorThreadCount = 1;
            }
            //如果poller线程数小于0,则默认为1
            if (pollerThreadCount <= 0) {
                //minimum one poller thread
                pollerThreadCount = 1;
            }
    
            //获取一个栅栏,通过AQS实现的一个共享锁
            setStopLatch(new CountDownLatch(pollerThreadCount));
    
            // Initialize SSL if needed
            //初始化ssl配置
            initialiseSsl();
       
            selectorPool.open();
        }
    1. 第一步初始化Socket线程

    1. 调用initServerSocket()方法,初始化Socket线程,可见这里线程为阻塞,目的能解释得通的也就是为了操作方便,但后续建立连接后的socket,会设为非阻塞的
    2. 初始化accptor与poller线程个数(分别为1 , 2) 
    3. 获取栅栏 CountDownLatch,用于在在高并发的场景时,允许并发请求个数最大值,默认 10000
    4. 这里需要提出一个Acceptor 和 Poller 的概念。Acceptor主要的职责就是监听是否有客户端套接字连接,并接收套接字,连接之后,把Socket 注册到Poller轮询器中的Selector中,如图

    总结:

      到此为止初始化的过程已经结束,大致过程也就是通过把公共的东西交给高度抽象的父类处理,然后父类去调用提供给子类实现的回调函数,此时利用组合模式,在每次初始化对象完毕之后,再将其持有的对象调用其共同的父类的初始化方法,这样便十分简洁。后续将继续回到StandardServer的父类调用init()方法完毕时继续往下走,执行 startInternal()方法,但大致也与init 时相同,便将核心的加载方法拿出来。

    8. NioEndpoint. startInternal()  启动Nio相关线程池

      

    @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());
    
                //创建工作的线程池,默认最小10,最大200
                // Create worker collection
                if ( getExecutor() == null ) {
                    createExecutor();
                }
    
                initializeConnectionLatch();
    
                //创建 2个 Poller 线程
                // Start poller threads
                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);
                    pollerThread.start();
                }
                //启动1个Acceptor线程
                startAcceptorThreads();
            }
        }
    1. 到此为止,Tomcat便已经启动完成。这里一共启动了 1个Acceptor线程,2个poller线程,10个work线程(启动时,不考虑有连接数并发超过10个任务)

  • 相关阅读:
    neo4j 图数据库
    eclipse 当中,两种添加插件的方法 .
    ubuntu16.04如何添加用root用户登录图形界面
    遇到Linux系统安装时窗口过大,按钮点不到,该怎么解决
    hadoop 搭建3节点集群,遇到Live Nodes显示为0时解决办法
    VMware 虚拟机克隆 CentOS 6.5 之后,网络配置问题的解决方案
    scala(13)-----集合(Collection)-------元组
    scala(13)-----集合(Collection)-------Map(映射)
    scala(13)-----集合(Collection)-------Set(集合)
    scala(13)-----集合(Collection)-------列表
  • 原文地址:https://www.cnblogs.com/coding400/p/10526993.html
Copyright © 2011-2022 走看看