Tomcat源码解析-组件之StandardServer

1 StandardServer职责

StandardServer是tomcat容器的最高层的组件,职责如下:
实现Tomcat一键启动关闭,管理全局 JDNI资源,管理子组件,阻塞tomcat主线程。

StandardServer子组件

Tomcat源码解析-组件之StandardServer_第1张图片
image

2 运行流程

2.1 Bootstarp运行流程

Bootstarp作为tomcat启动类,JVM会调用main函数完成tomcat启动。在其内部流程如下:

  • 1 实例化Bootstrap对象。

  • 2 对Bootstrap对象init,其内部包括构建Tomcat类加载器,并实例化Catalina对象

  • 3 以main函数args参数作为指令,通过调用Catalina类中不同的方法,完成相应的功能。

    • startd:首先调用load方法,接着调用start方法,
    • start:首先调用setAwait方法,接着调用load方法,最后调用start方法
    • stopd:调stopServer方法
    • configtest:调load方法
Tomcat源码解析-组件之StandardServer_第2张图片
image

核心方法功能

  • bootstrap.init():负责初始化Bootstrap,在初始化的过程最重要的就是构建Tomcat类加载器,并创建Catalina。
  • bootstrap.load(args):负责通过反射调用Catalina.load方法,实现tomcat加载。
  • bootstrap.start():负责通过反射调用Catalina.start方法,实现tomcat启动。
  • bootstrap.stop():负责通过反射调用Catalina.start方法,实现tomcat停止。
  • bootstrap.setAwait(true):负责通过反射调用Catalina.setAwait方法,设置tomcat是否需要启动一个Socket等待接受shutdown命令,用来停止Tomcat.
2.2 Catalina运行流程
Tomcat源码解析-组件之StandardServer_第3张图片
image
  • catalina.init():负责使用Digester解析Server.xml,并实例化Server.xml中描述的所有组件,之后调用调用Server组件初始化方法init().

  • catalina.start():负责调用Server组件start启动方法,之后给当前JVM进程注册一个钩子线程,该线程负责用来当tomcat被关闭时完成一些清理工作。最后调用Server组件await()方法阻塞当前线程(tomcat主线程),如果当前线程从await()方法返回表示tomcat被停止。

  • catalina.stop():负责向JVM清理掉注册的钩子线程,顺序调用Server组件停止方法stop()以及清理方法destroy()。

  • catalina.stopServer():负责顺序调用Server组件停止方法stop()以及清理方法destroy(),并向Tomcat指定的端口发送shutdown指令,停止tomcat主线程。

3 解析server.xml

Tomcat使用Digester解析server.xml,Digester是一款用于将xml转换为Java对象的事件驱动型工具,是对SAX的高层次的封装。相对于SAX可以为xml中每一个标签设置对应的解析规则。详见 Tomcat相关技术-Digester(二)

这里通过解析server.xml实例化StandardServer,并设置server.xml文件中定义的属性初始化。

server.xml配置


  
  
  
  
  

  
  
    
  

  
  
  
  ....

3.1 解析标签

标签用来表示当前StandardServer组件

//解析标签
/** 解析标签实例化StandardServer对象,并push到操作栈中 **/
digester.addObjectCreate("Server",
                         "org.apache.catalina.core.StandardServer",
                         "className");
                         
/** 解析标签将标签中属性值映射到StandardServer对象中**/  
digester.addSetProperties("Server");

/** 解析标签将操作栈栈顶对象设置到次栈顶对象属性中**/
//将StandardServer对象设置到Catalina启动类对象的server属性中
digester.addSetNext("Server",
                    "setServer",
                    "org.apache.catalina.Server");

StandardServer构造函数

public StandardServer() {
    super();
    globalNamingResources = new NamingResourcesImpl();
    globalNamingResources.setContainer(this);

    /** 判断是否开启JNDI服务 **/
    if (isUseNaming()) {
        namingContextListener = new NamingContextListener();
        addLifecycleListener(namingContextListener);
    } else {
        namingContextListener = null;
    }
}

标签属性映射到StandardServer对象属性中

/**
 * Tomcat shutdown操作,对应字符串指令
 */
private String shutdown = "SHUTDOWN";

/**
 * Tomcat ShutDown操作,服务端监听Socket端口号。
 */
private int port = 8005;

/**
 * Tomcat ShutDown执行,服务端监听Socket地址。
 */
private String address = "localhost";
Tomcat源码解析-组件之StandardServer_第4张图片
image
3.2 解析标签

标签中定义了全局JNDI资源,

//解析GlobalNamingResources>标签
/** 解析标签实例化NamingResourcesImpl对象,并push到操作栈中 **/
digester.addObjectCreate("Server/GlobalNamingResources",
                 "org.apache.catalina.deploy.NamingResourcesImpl");

/** 解析标签将标签中属性值映射到NamingResourcesImpl对象中**/                  
digester.addSetProperties("Server/GlobalNamingResources");


/** 解析标签将操作栈栈顶对象作为次栈顶对象StandardServer.setGlobalNamingResources方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/GlobalNamingResources",
            "setGlobalNamingResources",
            "org.apache.catalina.deploy.NamingResourcesImpl");
            

          
3.2.1 设置globalNamingResources属性
/**
 * 设置globalNamingResources属性
 */
@Override
public void setGlobalNamingResources
    (NamingResourcesImpl globalNamingResources) {

    /** 获取设置前globalNamingResources **/
    NamingResourcesImpl oldGlobalNamingResources =
        this.globalNamingResources;

    /** 设置globalNamingResources **/
    this.globalNamingResources = globalNamingResources;
    this.globalNamingResources.setContainer(this);

    /** 触发属性变更通知 **/
    support.firePropertyChange("globalNamingResources",
                               oldGlobalNamingResources,
                               this.globalNamingResources);
}
3.3 解析标签

标签中定义StandardServer组件中需要的LifecycleListener监听器。标签内可以设置多个

//解析标签
/** 解析标签实例化标签中className属性定义的对象,并push到操作栈中 **/
digester.addObjectCreate("Server/Listener",
                         null, // MUST be specified in the element
                         "className");
/** 解析标签将标签中属性值映射到其实例化对象中**/
digester.addSetProperties("Server/Listener");

/** 解析标签将操作栈栈顶对象作为次栈顶对象StandardServer.addLifecycleListener方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/Listener",
                    "addLifecycleListener",
                    "org.apache.catalina.LifecycleListener");

Server中定义的Listener

 
 
 
 
 

添加LifecycleListener监听器

/**
 * 给当前组件添加一个生命周期监听器
 */
@Override
public void addLifecycleListener(LifecycleListener listener) {
    lifecycleListeners.add(listener);
}
3.4 解析属性

标签中定义StandardServer组件中子组件Service。标签内可以设置多个

//解析标签
/** 解析标签实例化StandardService对象,并push到操作栈中 **/
digester.addObjectCreate("Server/Service",
                         "org.apache.catalina.core.StandardService",
                         "className");
/** 解析标签将标签中属性值映射到StandardService对象中**/
digester.addSetProperties("Server/Service");
/** 解析标签将操作栈栈顶对象作为次栈顶对象StandardServer.addService方法调用的参数,设置到StandardServer属性中**/
digester.addSetNext("Server/Service",
                    "addService",
                    "org.apache.catalina.Service");

添加Service子组件

/**
 * 将service子组件加到Tomcat Server组件中
 */
@Override
public void addService(Service service) {

    /** service 反向关联外部 Server组件 **/
    service.setServer(this);

    synchronized (servicesLock) {
        /** 将service组件添加到Server.ervices数组类型属性中 **/
        Service results[] = new Service[services.length + 1];
        System.arraycopy(services, 0, results, 0, services.length);
        results[services.length] = service;
        services = results;

        /** 如果当前Server组件已经启动,则启动添加Service组件 **/
        if (getState().isAvailable()) {
            try {
                service.start();
            } catch (LifecycleException e) {
                // Ignore
            }
        }

        /** 将service属性更改通知给监听器  **/
        support.firePropertyChange("service", null, service);
    }
}

4 实现一键启动

4.1 组件生命周期

StandardServer作为tomcat最上层的组件,和其他所有组件一样都实现了Lifecycle 接口。

public interface Lifecycle {
    ....
    // 初始化方法
    public void init() throws LifecycleException;
    // 启动方法
    public void start() throws LifecycleException;
    // 停止方法,和start对应
    public void stop() throws LifecycleException;
    // 销毁方法,和init对应
    public void destroy() throws LifecycleException;
    // 获取生命周期状态
    public LifecycleState getState();
    // 获取字符串类型的生命周期状态
    public String getStateName();
}

Tomcat 定义一个基类LifecycleBase 来实现 Lifecycle 接口,把一些公共的逻辑放到基类中去,比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等,而子类就负责实现自己的初始化、启动和停止等模板方法。为了避免跟基类中的方法同名,我们把具体子类的实现方法改个名字,在后面加上 Internal,叫 initInternal、startInternal 等。

/**
 *  组件初始化动作,所有组件通用操作
 *   1 检查校验当前组件状态是否能够初始化
 *   2 修改当前的状态从 NEW-->INITIALIZING
 *   3 调用每个组件模板方法实现完成初始化动作
 *   4 修改当前的状态从 INITIALIZING-->INITIALIZED
 */
@Override
public final synchronized void init() throws LifecycleException {
    /** 非NEW状态,不允许调用init()方法 **/
    if (!state.equals(LifecycleState.NEW)) {
        /** 从sm获取"lifecycleBase.invalidTransition"属性对应日志格式,抛出LifecycleException异常 **/
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        /** 初始化逻辑之前,将状态变更为`INITIALIZING` **/
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        /** 初始化组件,该方法为一个abstract模板方法,需要组件自行实现  **/
        initInternal();
        /** 初始化完成之后,状态变更为`INITIALIZED`  **/
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    /** 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED` **/
    catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
}

/**
 * 初始化模板方法
 */
protected abstract void initInternal() throws LifecycleException;

其他模板方法

/**
 * 启动模板方法
 */
protected abstract void startInternal() throws LifecycleException;

/**
 * 停止模板方法
 */
protected abstract void stopInternal() throws LifecycleException;
    
/**
 * 销毁模板方法
 */
protected abstract void destroyInternal() throws LifecycleException;

详见Tomcat架构设计-组件生命周期

4.2 初始化StandardServer组件

StandardServer组件初始化的核心是调用所有Service子组件初始化方法init。

详细流程如下:

  • 1 将StringCache类型对象注册到JMX bean中

  • 2 将MBeanFactory类型对象注册到JMX bean中

  • 3 JNDI服务初始化

  • 4 读取Shared类加载器 管理的jar文件,将包含MANIFEST的JAR文件,添加到容器的清单资源中

  • 5 调用所有Service子组件初始化方法init

@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //1 将StringCache类型对象注册到JMX bean中
    onameStringCache = register(new StringCache(), "type=StringCache");

    //2 将MBeanFactory类型对象注册到JMX bean中
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");

    /** 3 JNDI服务初始化 **/
    globalNamingResources.init();


    /** 4 读取Shared类加载器 管理的jar文件,将包含MANIFEST的JAR文件,添加到容器的清单资源中 **/
    if (getCatalina() != null) {
        /** 获取Shared类加载器 **/
        ClassLoader cl = getCatalina().getParentClassLoader();
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        try {
                            File f = new File (url.toURI());
                            if (f.isFile() &&
                                    f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException e) {
                            // Ignore
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    /** 5 调用所有Service子组件初始化方法init **/
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}
4.3 启动StandardServer组件

启动start的核心是调用所有Service子组件初始化方法start。

详细流程如下:

  • 1 通知LifecycleListener监听器当前组件触发 CONFIGURE_START_EVENT事件

  • 2 更正当前组件状态为STARTING

  • 3 启动JNDI服务

  • 4 调用所有Service子组件启动方法start

/**
 * 组件启动模板方法实现
 */
@Override
protected void startInternal() throws LifecycleException {

    /** 通知监听器当前组件触发 CONFIGURE_START_EVENT事件 **/
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    /** 更正当前组件状态为STARTING  **/
    setState(LifecycleState.STARTING);
    /** 启动JNDI服务 **/
    globalNamingResources.start();

    /** 启动所有service组件 **/
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}
4.4 停止StandardServer组件

启动stop的核心是调用所有Service子组件初始化方法stop。

详细流程如下:

  • 1 通知LifecycleListener监听器当前组件触发 CONFIGURE_STOP_EVENT事件

  • 2 更正当前组件状态为STOPPING

  • 3 关闭JNDI服务

  • 4 调用所有Service子组件启动方法stop

  • 5 调用 stopAwait

    • 1 设置stopAwait标识为true,stopAwait用来判断tomcat主线程是否要退出
    • 2 关闭Socket服务,不在监听shutdown命令
/**
 * 组件停止模板方法实现
 */
@Override
protected void stopInternal() throws LifecycleException {


    /** 通知监听器当前组件触发 CONFIGURE_STOP_EVENT事件 **/
    fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);

    /** 更正当前组件状态为STOPPING  **/
    setState(LifecycleState.STOPPING);

    /** 关闭所有service组件 **/
    for (int i = 0; i < services.length; i++) {
        services[i].stop();
    }

    /** 关闭JNDI服务 **/
    globalNamingResources.stop();

    /** 停止监听 shutdown命令 Socket服务 **/
    stopAwait();
}
4.5 销毁StandardServer组件

销毁destroy的核心是调用所有Service子组件初始化方法destroy。

1 调用所有Service子组件启动方法destroy

2 销毁JND全局资源

3 jmx bean注销MBeanFactory

4 jmx bean注销StringCache

5 调用LifecycleMBeanBase.destroyInternal 将当前组件对象从jmx 注销

/**
 * 组件销毁模板方法实现
 */
@Override
protected void destroyInternal() throws LifecycleException {
    /** 调用所有Service子组件启动方法destroy **/
    for (int i = 0; i < services.length; i++) {
        services[i].destroy();
    }
    /** 销毁JND全局资源 **/
    globalNamingResources.destroy();

    /** jmx bean注销MBeanFactory **/
    unregister(onameMBeanFactory);

    /** jmx bean注销StringCache **/
    unregister(onameStringCache);

    /** 调用LifecycleMBeanBase.destroyInternal
     * 将当前组件对象从jmx 注销
     */
    super.destroyInternal();
}
4.6 StandardServer实现一键启动

在这样的设计中,在父组件的 init 方法里需要创建子组件并调用子组件的 init 方法。同样,在父组件的 start 方法里也需要调用子组件的 start 方法。只要调用最顶层组件StandardServer的 init 和 start 方法,整个 Tomcat 就被启动起来了。只要调用最顶层组件StandardServer的 destroy 和 stop 方法,整个 Tomcat 就被关闭。

Tomcat源码解析-组件之StandardServer_第5张图片
image

5 实现子组件Service管理

5.1 addService

添加已在解析server.xml初始化设置调用

/**
     * 将service子组件加到Tomcat Server组件中
     */
    @Override
    public void addService(Service service) {

        /** service 反向关联外部 Server组件 **/
        service.setServer(this);

        synchronized (servicesLock) {
            /** 将service组件添加到Server.ervices数组类型属性中 **/
            Service results[] = new Service[services.length + 1];
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;

            /** 如果当前Server组件已经启动,则启动添加Service组件 **/
            if (getState().isAvailable()) {
                try {
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore
                }
            }

            /** 将service属性更改通知给监听器  **/
            support.firePropertyChange("service", null, service);
        }

    }
5.2 返回指定名称Service组件
@Override
public Service findService(String name) {
        if (name == null) {
            return (null);
        }
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                if (name.equals(services[i].getName())) {
                    return (services[i]);
                }
            }
        }
        return (null);
}
5.3 返回Server组件所有Service子组件
    /**
     * 返回Server组件所有Service子组件
     */
    @Override
    public Service[] findServices() {

        return services;

    }
5.4 从Server组件中删除Service子组件
/**
 * 从Server组件中删除Service子组件
 */
@Override
public void removeService(Service service) {

    synchronized (servicesLock) {
        /** 从Service子组件数组找到删除 service 子组件 **/
        int j = -1;
        for (int i = 0; i < services.length; i++) {
            if (service == services[i]) {
                j = i;
                break;
            }
        }
        /** 没有找到忽略此动作 **/
        if (j < 0)
            return;

        /** 对删除service子组件 停止动作 **/
        try {
            services[j].stop();
        } catch (LifecycleException e) {
            // Ignore
        }

        /** 对service 数组中在删除service子组件后service子组件在数组中前移 **/
        int k = 0;
        Service results[] = new Service[services.length - 1];
        for (int i = 0; i < services.length; i++) {
            if (i != j)
                results[k++] = services[i];
        }
        services = results;

        /** support通知service属性变更 **/
        support.firePropertyChange("service", service, null);
    }
}

6 阻塞tomcat主线程

Tomcat源码解析-组件之StandardServer_第6张图片
image

阻塞tomcat主线程,只要stopAwait不为true, tomcat主线程在此无限循环.监听到客户端发起SHUTDOWN命令后退出

@Override
    public void await() {
        if( port == -2 ) {
            return;
        }
        if( port==-1 ) {
            try {
                awaitThread = Thread.currentThread();
                while(!stopAwait) {
                    try {
                        Thread.sleep( 10000 );
                    } catch( InterruptedException ex ) {
                        // continue and check the flag
                    }
                }
            } finally {
                awaitThread = null;
            }
            return;
        }

        /**  创建服务端监听shutdown命令 Socket **/
        try {
            awaitSocket = new ServerSocket(port, 1,
                    InetAddress.getByName(address));
        } catch (IOException e) {
            log.error("StandardServer.await: create[" + address
                               + ":" + port
                               + "]: ", e);
            return;
        }

        try {
            /** 获取当前线程 **/
            awaitThread = Thread.currentThread();

            /** 只要stopAwait不为true, tomcat主线程在此无限循环.监听到客户端发起SHUTDOWN命令后退出 **/
            while (!stopAwait) {
                ServerSocket serverSocket = awaitSocket;
                if (serverSocket == null) {
                    break;
                }

                // Wait for the next connection
                Socket socket = null;
                StringBuilder command = new StringBuilder();
                try {
                    /** 监听阻塞当前线程 **/
                    InputStream stream;
                    long acceptStartTime = System.currentTimeMillis();
                    try {
                        socket = serverSocket.accept();
                        socket.setSoTimeout(10 * 1000);  // Ten seconds
                        stream = socket.getInputStream();
                    } catch (SocketTimeoutException ste) {
                        log.warn(sm.getString("standardServer.accept.timeout",
                                Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste);
                        continue;
                    } catch (AccessControlException ace) {
                        log.warn("StandardServer.accept security exception: "
                                + ace.getMessage(), ace);
                        continue;
                    } catch (IOException e) {
                        if (stopAwait) {
                            break;
                        }
                        log.error("StandardServer.await: accept: ", e);
                        break;
                    }

                    /** 发生指令的字符数大于1024,则最大读取字符扩容到
                     * expected += (random.nextInt() % 1024)
                     */
                    int expected = 1024; // Cut off to avoid DoS attack
                    while (expected < shutdown.length()) {
                        if (random == null)
                            random = new Random();
                        expected += (random.nextInt() % 1024);
                    }

                    /** 读取指令字符串 **/
                    while (expected > 0) {
                        int ch = -1;
                        try {
                            ch = stream.read();
                        } catch (IOException e) {
                            log.warn("StandardServer.await: read: ", e);
                            ch = -1;
                        }
                        /** 遍历到控制字符或EOF(-1)终止读取 **/
                        if (ch < 32 || ch == 127) {
                            break;
                        }
                        command.append((char) ch);
                        expected--;
                    }
                } finally {
                    // Close the socket now that we are done with it
                    try {
                        if (socket != null) {
                            socket.close();
                        }
                    } catch (IOException e) {
                        // Ignore
                    }
                }

                /**  发生执行是否为 shutdown指令字符串相同,相同则跳出循环Tomcat主线程退出**/
                boolean match = command.toString().equals(shutdown);
                if (match) {
                    log.info(sm.getString("standardServer.shutdownViaPort"));
                    break;
                } else
                    log.warn("StandardServer.await: Invalid command '"
                            + command.toString() + "' received");
            }
        } finally {
            ServerSocket serverSocket = awaitSocket;
            awaitThread = null;
            awaitSocket = null;

            // Close the server socket and return
            if (serverSocket != null) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

你可能感兴趣的:(Tomcat源码解析-组件之StandardServer)