Tomcat源码导读 - Connector,Container

2019-09-25  本文已影响0人  minute_5

源码导读

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技术内幕 - 许令波
上一篇 下一篇

猜你喜欢

热点阅读