美文网首页
Tomcat源码导读 - Connector,Container

Tomcat源码导读 - Connector,Container

作者: minute_5 | 来源:发表于2019-09-25 14:40 被阅读0次

    源码导读

    Connector

        // connector 通过protocolHandler来实现具体的请求处理
        // 每种ProtocolHandler包含3个重要组件来处理请求: 
        // 1. Adapter:用于将封装好的Request交给Container,将请求适配到servlet容器。请求响应对象从连接器传送到容器需要一个桥梁CoyoteAdapter 。
        // 2. Endpoint:用于处理底层Socket连接(nio和nio2实现的是TCP/IP协议,Apr实现的是SSL/TLS协议) 
        // 3. Processer:用于将Endpoint接收到的Socket封装成Request(实现HTTP协议或websocket协议或AJP协议)
        protected final ProtocolHandler protocolHandler;
    
        protected void startInternal() throws LifecycleException {
    
            // Validate settings before starting
            if (getPortWithOffset() < 0) {
                throw new LifecycleException(sm.getString(
                        "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
            }
    
            setState(LifecycleState.STARTING);
    
            try {
                protocolHandler.start();
            } catch (Exception e) {
                throw new LifecycleException(
                        sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
            }
        }
    
        // AbstractProtocol impls ProtocolHandler
        // 其有两个实现类AbstractHttp11Protocol与AbstractAjpProtocol分别对应了具体协议的实现,这两个类其下的子类分别实现了NIO,NIO2,与Apr(Apache portable Run-time libraries) - 从操作系统级别解决异步IO问题,apache tomcat自己的native技术。
    
        // 每一个endpoint都代表了不同协议的不同IO数据处理方式,比如使用http11的nio2
        // 一个endpoint实例对应着一个ServerSocketChannel通道
        // 其一共三个实现类NIO,NIO2,与Ajp自己的Apr
        /**
         * Endpoint that provides low-level network I/O - must be matched to the
         * ProtocolHandler implementation (ProtocolHandler using NIO, requires NIO
         * Endpoint etc.).
         */
        private final AbstractEndpoint<S,?> endpoint;
    
        public void start() throws Exception {
            if (getLog().isInfoEnabled()) {
                getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
                logPortOffset();
            }
    
            endpoint.start();
            monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                    new Runnable() {
                        @Override
                        public void run() {
                            if (!isPaused()) {
                                startAsyncTimeout();
                            }
                        }
                    }, 0, 60, TimeUnit.SECONDS);
        }
    
        public final void start() throws Exception {
            if (bindState == BindState.UNBOUND) {
                bindWithCleanup();
                bindState = BindState.BOUND_ON_START;
            }
            startInternal();
        }
    
        // 以NIO为例
         /**
         * Start the NIO endpoint, creating acceptor, poller threads.
         */
        @Override
        public void startInternal() throws Exception {
    
            if (!running) {
                running = true;
                paused = false;
    
                // 初始化各类缓存
                if (socketProperties.getProcessorCache() != 0) {
                    processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getProcessorCache());
                }
                if (socketProperties.getEventCache() != 0) {
                    eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getEventCache());
                }
                if (socketProperties.getBufferPool() != 0) {
                    nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                            socketProperties.getBufferPool());
                }
    
                // Create worker collection
                // 初始化线程池
                if (getExecutor() == null) {
                    createExecutor();
                }
    
                // 初始化最大连接数 默认10000
                initializeConnectionLatch();
    
                // Start poller thread
                // 线程内部类,启动线程监听是否有请求,有则处理,没有则循环等待
                poller = new Poller();
                Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
    
                // 启动acceptor线程接收 socket
                // 默认只有一个acceptor线程,用于接收请求,并将请求信息封装为PollEvent对象放入PollEvent待处理队列中。如果端点处于暂停状态,50s探测一次,否则连接数+1,并判断是否超最大连接数(LimitLantch实现),如果超了,阻塞等待。
                // ServerSocketChannel.accept()等待新的请求将SocketChannel请求信息封装为NioChannel(NioChannel从缓存中读取,没有就新生成),将NioChannel封装为PollerEvent(PollerEvent从缓存中读取,没有就新生成)
                startAcceptorThread();
            }
        }
    

    Container

    1. StandardEngine、StandardContext、StandardHost、StandardWrapper都继承 了ContainerBase类,ContainerBase内部类ContainerBackgroundProcessor会周期的执行 run(), 它是运行在一个后台线程中, run()会周期调用所有容器的 backgroundProcess 方法
    2. backgroundProcess方法主要通过四个组件完成工作:
      1. cluster:Catalina的集群组件,实现集群应用部署,多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。 它监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员。
      2. loader Catalina在启动过程中创建的classloader的实例,查看Context容器是否需要重新加载,热部署就是利用这个机制来完成的
      3. manager Catalina中的Session管理器,主要的功能是将过期会话(session)置为无效
      4. realm Catalina中的安全组件,可以用来管理资源的访问权限 pipeline遍历并调用容器pipeline的valve链表的backgroundProcess
        // stanard engine, 添加的子容器只能是Host类型的
        public void addChild(Container child) {
    
            if (!(child instanceof Host))
                throw new IllegalArgumentException
                    (sm.getString("standardEngine.notHost"));
            super.addChild(child);
    
        }
    
        // standard context, start context的时候会调用LifecycleBase的start方法,从而调用context的实现方法startInternal
        // 主要作用是设置资源与各种管理组件,启动子容器与pipleline
        // 代码太长,截出下面的关键代码
        protected synchronized void startInternal() throws LifecycleException {
            ... 
    
            // 
            if (ok) {
                resourcesStart();
            }
    
            if (getLoader() == null) {
                WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
                webappLoader.setDelegate(getDelegate());
                setLoader(webappLoader);
            }
    
            // An explicit cookie processor hasn't been specified; use the default
            if (cookieProcessor == null) {
                cookieProcessor = new Rfc6265CookieProcessor();
            }
    
           ...
    
            try {
                if (ok) {
                    // Start our subordinate components, if any
                    Loader loader = getLoader();
                    if (loader instanceof Lifecycle) {
                        ((Lifecycle) loader).start();
                    }
    
                    ...
    
                    Realm realm = getRealmInternal();
                    if(null != realm) {
                        if (realm instanceof Lifecycle) {
                            ((Lifecycle) realm).start();
                        }
                        ...
                    }
    
                    // Notify our interested LifecycleListeners
                    fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
    
                    // Start our child containers, if not already started
                    for (Container child : findChildren()) {
                        if (!child.getState().isAvailable()) {
                            child.start();
                        }
                    }
    
                    // Start the Valves in our pipeline (including the basic),
                    // if any
                    if (pipeline instanceof Lifecycle) {
                        ((Lifecycle) pipeline).start();
                    }
    
                    // Acquire clustered manager
                    Manager contextManager = null;
                    Manager manager = getManager();
    
                    ...
    
                    // Configure default manager if none was specified
                    if (contextManager != null) {
                        if (log.isDebugEnabled()) {
                            log.debug(sm.getString("standardContext.manager",
                                    contextManager.getClass().getName()));
                        }
                        setManager(contextManager);
                    }
    
                    if (manager!=null && (getCluster() != null) && distributable) {
                        //let the cluster know that there is a context that is distributable
                        //and that it has its own manager
                        getCluster().registerManager(manager);
                    }
                }
                
                ...
    
                // We put the resources into the servlet context
                if (ok)
                    getServletContext().setAttribute
                        (Globals.RESOURCES_ATTR, getResources());
    
                if (ok ) {
                    if (getInstanceManager() == null) {
                        setInstanceManager(createInstanceManager());
                    }
                    getServletContext().setAttribute(
                            InstanceManager.class.getName(), getInstanceManager());
                    InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager());
                }
    
                // Create context attributes that will be required
                if (ok) {
                    getServletContext().setAttribute(
                            JarScanner.class.getName(), getJarScanner());
                }
    
                ...
    
                try {
                    // Start manager
                    Manager manager = getManager();
                    if (manager instanceof Lifecycle) {
                        ((Lifecycle) manager).start();
                    }
                } catch(Exception e) {
                    log.error(sm.getString("standardContext.managerFail"), e);
                    ok = false;
                }
    
                ...
    
                // Start ContainerBackgroundProcessor thread
                super.threadStart();
            } finally {
                // Unbinding thread
                unbindThread(oldCCL);
            }
    
           ...
    
            // Reinitializing if something went wrong
            if (!ok) {
                setState(LifecycleState.FAILED);
            } else {
                setState(LifecycleState.STARTING);
            }
        }
    
        // stanard Wrapper
    

    启动类

    Tomcat启动类图.jpg
        /**
         * Main method and entry point when starting Tomcat via the provided
         * scripts.
         *
         * @param args Command line arguments to be processed
         */
        public static void main(String args[]) {
    
            synchronized (daemonLock) {
                if (daemon == null) {
                    // Don't set daemon until init() has completed
                    Bootstrap bootstrap = new Bootstrap();
                    try {
                        // 初始化加载
                        bootstrap.init();
                    } catch (Throwable t) {
                        handleThrowable(t);
                        t.printStackTrace();
                        return;
                    }
                    daemon = bootstrap;
                } else {
                    // When running as a service the call to stop will be on a new
                    // thread so make sure the correct class loader is used to
                    // prevent a range of class not found exceptions.
                    Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
                }
            }
    
            try {
                String command = "start";
                if (args.length > 0) {
                    command = args[args.length - 1];
                }
                // 监听各种命令,然后通过daemon.load(args)反射的方式调用
                if (command.equals("startd")) {
                    args[args.length - 1] = "start";
                    daemon.load(args);
                    daemon.start();
                } else if (command.equals("stopd")) {
                    args[args.length - 1] = "stop";
                    daemon.stop();
                } else if (command.equals("start")) {
                    daemon.setAwait(true);
                    daemon.load(args);
                    daemon.start();
                    if (null == daemon.getServer()) {
                        System.exit(1);
                    }
                } else if (command.equals("stop")) {
                    daemon.stopServer(args);
                } else if (command.equals("configtest")) {
                    daemon.load(args);
                    if (null == daemon.getServer()) {
                        System.exit(1);
                    }
                    System.exit(0); 
                } else {
                    log.warn("Bootstrap: command \"" + command + "\" does not exist.");
                }
            } catch (Throwable t) {
                // Unwrap the Exception for clearer error reporting
                if (t instanceof InvocationTargetException &&
                        t.getCause() != null) {
                    t = t.getCause();
                }
                handleThrowable(t);
                t.printStackTrace();
                System.exit(1);
            }
        }
    
        // 初始化方法
        public void init() throws Exception {
    
            //初始化三个类加载器 commonLoader、catalinaLoader、sharedLoader
            initClassLoaders();
    
            // 给当前线程设置类加载器
            Thread.currentThread().setContextClassLoader(catalinaLoader);
    
            // 通过catalinaLoader去加载tomcat各个Package中的类
            SecurityClassLoad.securityClassLoad(catalinaLoader);
    
            // Load our startup class and call its process() method
            if (log.isDebugEnabled())
                log.debug("Loading startup class");
            Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
            Object startupInstance = startupClass.getConstructor().newInstance();
    
            // 通过反射给启动类实例 设置父加载器 sharedLoader
            // Set the shared extensions class loader
            if (log.isDebugEnabled())
                log.debug("Setting startup class properties");
            String methodName = "setParentClassLoader";
            Class<?> paramTypes[] = new Class[1];
            paramTypes[0] = Class.forName("java.lang.ClassLoader");
            Object paramValues[] = new Object[1];
            paramValues[0] = sharedLoader;
            Method method =
                startupInstance.getClass().getMethod(methodName, paramTypes);
            method.invoke(startupInstance, paramValues);
    
            catalinaDaemon = startupInstance;
        }
    
        // 容器 监听到start时,调用了Catatina的load
        public void load() {
    
            if (loaded) {
                return;
            }
            loaded = true;
    
            long t1 = System.nanoTime();
    
            // 创建初始化目录
            initDirs();
    
            // Before digester - it may be needed
            // 初始化命名系统(initNaming)向System.setProperty注入java.naming.factory.initial类,
            // 用于上下文查询的工厂类 org.apache.naming.java.javaURLContextFactory
            initNaming();
    
            // Set configuration source
            ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
            File file = configFile();
    
            // Create and execute our Digester
            // 创建一个digester对象。Digester主要是将XML映射成Java类,简化XML的处理。
            Digester digester = createStartDigester();
    
            // digester通过parse方法解析server.xml的输入流(inputSource)
            // 从而生成Server对象,并将如Engine、Host、Context、Wrapper包含的子容器等相关组件加到对象中。
            try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
                InputStream inputStream = resource.getInputStream();
                InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
                inputSource.setByteStream(inputStream);
                digester.push(this);
                digester.parse(inputSource);
            } catch (Exception e) {
                log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
                if (file.exists() && !file.canRead()) {
                    log.warn(sm.getString("catalina.incorrectPermissions"));
                }
                return;
            }
    
            getServer().setCatalina(this);
            getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
            getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
            // Stream redirection
            // System.out and System.err 的输出流重定向处理
            initStreams();
    
            // Start the new server
            try {
                getServer().init();
            } catch (LifecycleException e) {
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new java.lang.Error(e);
                } else {
                    log.error(sm.getString("catalina.initError"), e);
                }
            }
    
            long t2 = System.nanoTime();
            if (log.isInfoEnabled()) {
                log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
            }
        }
    

    参考:

    Tomcat9的容器 - 森林之王
    深入分析Java Web技术内幕 - 许令波

    相关文章

      网友评论

          本文标题:Tomcat源码导读 - Connector,Container

          本文链接:https://www.haomeiwen.com/subject/tktcuctx.html