首页 > 代码库 > 深入理解 JBoss 7/WildFly Domain 模式启动过程

深入理解 JBoss 7/WildFly Domain 模式启动过程

概述

JBoss 7/WildFly 以 domain 模式启动时会启动多个 JVM。比如例如以下通过启动脚本启动 domain 模式:

./domain.sh

启动后我们查看进程:

[kylin@localhost tdump]$ jps -l
23655 /home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar
23671 /home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar
23736 /home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar

我们能够发现 domain 模式启动时会启动后,有三个进程(相应三个JVM)执行。本文主要研究 domain 模式下是怎样启动多个 JVM 的。

另外,`jboss-modules.jar` 是 JBoss 底层类载入机制,用于类载入和启动一个 JVM。


从启动脚本開始

domain.sh 脚本中例如以下信息:

      eval \"$JAVA\" -D\"[Process Controller]\" $PROCESS_CONTROLLER_JAVA_OPTS          \"-Dorg.jboss.boot.log.file=$JBOSS_LOG_DIR/process-controller.log\"          \"-Dlogging.configuration=file:$JBOSS_CONFIG_DIR/logging.properties\"          -jar \"$JBOSS_HOME/jboss-modules.jar\"          -mp \"${JBOSS_MODULEPATH}\"          org.jboss.as.process-controller          -jboss-home \"$JBOSS_HOME\"          -jvm \"$JAVA_FROM_JVM\"          -mp \"${JBOSS_MODULEPATH}\"          --          \"-Dorg.jboss.boot.log.file=$JBOSS_LOG_DIR/host-controller.log\"          \"-Dlogging.configuration=file:$JBOSS_CONFIG_DIR/logging.properties\"          $HOST_CONTROLLER_JAVA_OPTS          --          -default-jvm \"$JAVA_FROM_JVM\"          ‘"$@"‘
      JBOSS_STATUS=$?

基于 JBoss Module 类载入机制,我们查看 org.jboss.as.process-controller,在 `module.xml` 的描写叙述中启动 Main 方法例如以下:

<module xmlns="urn:jboss:module:1.1" name="org.jboss.as.process-controller">
    <properties>
        <property name="jboss.api" value="http://www.mamicode.com/private"/>
    </properties>

    <main-class name="org.jboss.as.process.Main"/>

所以我们能够从 `org.jboss.as.process.Main` 类開始,JBoss 是全然开源的。这样非常easy開始调试代码。通过例如以下步骤能够開始调试启动代码:

  • 在 pom.xml 中加入例如以下依赖

                <dependency>
                        <groupId>org.jboss.as</groupId>
                        <artifactId>jboss-as-process-controller</artifactId>
                        <version>7.2.0.Final-redhat-8</version>
                </dependency>

  • 编辑 domain.conf,加入例如以下配置

PROCESS_CONTROLLER_JAVA_OPTS="$PROCESS_CONTROLLER_JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=8788,server=y,suspend=y"
HOST_CONTROLLER_JAVA_OPTS="$HOST_CONTROLLER_JAVA_OPTS -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=y"

  • 以 domain 模式启动 JBoss。在 `org.jboss.as.process.Main` 的 main 方法中加入断点。開始代码调试,例如以下图所看到的:

技术分享

对比之前的启动脚本。main 方法传入的參数例如以下:

-jboss-home, /home/kylin/work/eap/jboss-eap-6.1,
-jvm, /usr/java/jdk1.7.0_21/bin/java,
-mp, /home/kylin/work/eap/jboss-eap-6.1/modules,
--,
-Dorg.jboss.boot.log.file=/home/kylin/work/eap/jboss-eap-6.1/domain/log/host-controller.log,
-Dlogging.configuration=file:/home/kylin/work/eap/jboss-eap-6.1/domain/configuration/logging.properties,
-server, -Xms63m, -Xmx512m, -XX:MaxPermSize=256m, -Djava.net.preferIPv4Stack=true, -Djboss.modules.system.pkgs=org.jboss.byteman,
-Djava.awt.headless=true, -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=y,
--,
-default-jvm, /usr/java/jdk1.7.0_21/bin/java

domain 模式启动

下图描写叙述 domain 模式启动的过程:

技术分享

依据上图描写叙述,我们将 domain 模式启动描写叙述为以下步骤:

  • 通过启动脚本 `domain.sh` 启动 *Process Controller* JVM,*Process Controller* 启动后监听于端口 0。该段口等待 Host Controller JVM 连接
  • Process Controller 启动 Host Controller。我们随后具体介绍 Process Controller 怎样启动 Host Controller, Host Controller 启动后。首先解析 `host.xml` 文件。获取要启动 Server 信息。并与 Process Controller 建立 TCP 连接,将启动 Server 的信息发送给 Process Controller
  • Process Controller 依据 Host Controller 发送的 Server 信息,启动相关的 Server
如上图。我们能够看到三个 JVM 启动的 Main 方法分别位于三个不同的 module 中: `org.jboss.as.process-controller` (相应的 Main 方法为 `org.jboss.as.process.Main`), `org.jboss.as.host-controller ` (相应的 Main 方法为 `org.jboss.as.host.controller.Main`), `org.jboss.as.server` (相应的 Main 方法为 `org.jboss.as.server.DomainServerMain`)。

Process Controller 怎样启动 Host Controller

Process Controller 和 Host Controller 属于不同的 JVM。Process Controller 启动 Host Controller 是在一个 JVM 中启动还有一个 JVM,启动代码位于 `org.jboss.as.process.ManagedProcess` 例如以下图所看到的:

技术分享

我们能够通过例如以下代码模拟:

        public static void main(String[] args) throws IOException {
                
                 List<String> command = new ArrayList<String>();
                 command.add("/usr/java/jdk1.7.0_21/bin/java");
                 command.add("-jar");
                 command.add("/home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar");
                 command.add("-version");
                 ProcessBuilder builder = new ProcessBuilder(command);
                 builder.directory(new File("/home/kylin/tmp"));
                 Process process = builder.start();
                 final InputStream stdout = process.getInputStream();
                 Thread stdoutThread = new Thread(new ReadTask(stdout, System.out));
                 stdoutThread.start();
        }

点击查看完整代码:org/wildfly/domain/test/PCStartHC.java

执行如上代码输出JBoss EAP 6.1 所使用的 JBoss Module 的版本:

[Host Controller] JBoss Modules version 1.2.0.Final-redhat-1

而上面调试过程中 ProcessBuilder 传入的參数例如以下:

/usr/java/jdk1.7.0_21/bin/java, -D[Host Controller],
-Dorg.jboss.boot.log.file=/home/kylin/work/eap/jboss-eap-6.1/domain/log/host-controller.log,
-Dlogging.configuration=file:/home/kylin/work/eap/jboss-eap-6.1/domain/configuration/logging.properties,
-server, -Xms63m, -Xmx512m, -XX:MaxPermSize=256m, -Djava.net.preferIPv4Stack=true, -Djboss.modules.system.pkgs=org.jboss.byteman,
-Djava.awt.headless=true, -agentlib:jdwp=transport=dt_socket,address=8787,server=y,suspend=y,
-jar, /home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar,
-mp, /home/kylin/work/eap/jboss-eap-6.1/modules,
-jaxpmodule, javax.xml.jaxp-provider, org.jboss.as.host-controller,

-mp, /home/kylin/work/eap/jboss-eap-6.1/modules,
--pc-address, 127.0.0.1,
--pc-port, 50396,
-default-jvm, /usr/java/jdk1.7.0_21/bin/java,
-Djboss.home.dir=/home/kylin/work/eap/jboss-eap-6.1

如上參数被分为两个部分,上面部分用来启动 Host Controller。以下部分作为 Main 方法參数。传递给 org.jboss.as.host-controller 模块定义的 main-class 中,>查看 main-class 定义例如以下:

<module xmlns="urn:jboss:module:1.1" name="org.jboss.as.host-controller">
    <properties>
        <property name="jboss.api" value="http://www.mamicode.com/private"/>
    </properties>

    <main-class name="org.jboss.as.host.controller.Main"/>

    <resources>
        <resource-root path="jboss-as-host-controller-7.2.0.Final-redhat-8.jar"/>

如上,我们能够在 `org.jboss.as.host.controller.Main` 中设定断点,继续调试 Host Controller 启动,例如以下

技术分享

`org.jboss.as.host.controller.Main` 中传入的參数例如以下:

-mp, /home/kylin/work/eap/jboss-eap-6.1/modules,
--pc-address, 127.0.0.1,
--pc-port, 45210,
-default-jvm, /usr/java/jdk1.7.0_21/bin/java,
-Djboss.home.dir=/home/kylin/work/eap/jboss-eap-6.1

注意,如前面分析,这些參数是从 Process Controller 传递过来的,且通过代码调试显示这些參数与之前分析的同样。

Process Controller 启动 Server

Process Controller 启动 Server 过程全然相似,仅仅是要启动的 Server 信息是通过 Host Controller传递过来的,传递过来的 ProcessBuilder 传入的參数例如以下:

/usr/java/jdk1.7.0_21/bin/java, -D[Server:server-one],
-XX:PermSize=256m, -XX:MaxPermSize=256m, -Xms1303m, -Xmx1303m, -server, -D[Host Controller]=true,
-Djava.awt.headless=true, -Djboss.modules.system.pkgs=org.jboss.byteman, -Djboss.home.dir=/home/kylin/work/eap/jboss-eap-6.1,
-Djava.net.preferIPv4Stack=true, -Djboss.server.log.dir=/home/kylin/work/eap/jboss-eap-6.1/domain/servers/server-one/log,
-Djboss.server.temp.dir=/home/kylin/work/eap/jboss-eap-6.1/domain/servers/server-one/tmp,
-Djboss.server.data.dir=/home/kylin/work/eap/jboss-eap-6.1/domain/servers/server-one/data,
-Dlogging.configuration=file:/home/kylin/work/eap/jboss-eap-6.1/domain/servers/server-one/data/logging.properties,
-jar, /home/kylin/work/eap/jboss-eap-6.1/jboss-modules.jar,
-mp, /home/kylin/work/eap/jboss-eap-6.1/modules, -jaxpmodule, javax.xml.jaxp-provider, org.jboss.as.server

通过 `org.jboss.as.server` 我们能够找到 Server JVM 启动的 Main 方法为 `org.jboss.as.server.DomainServerMain`。

怎样理解 PC。HC,Server

PC 是指 Process Controller。HC 是指 Host Controller(与 HC 相相应的是 Domain Controller,简称 DC)。Server 指的是 JBoss 服务器。

从功能层面来说,PC 用来启动和停止 HC 和 Server,且PC 启动停止 Server 首先须要得到 HC 发来的指令。domain 模式启动完毕后会启动例如以下线程用来停止 HC 和 Servers:

"reaper for Host Controller" prio=10 tid=0x7fc3b000 nid=0x770f in Object.wait() [0x803ab000]
"reaper for Server:server-one" prio=10 tid=0x8320b400 nid=0x7750 in Object.wait() [0x7fdfe000]

PC 启动 HC 与 Server 是在一个 JVM 中启动还有一个 JVM,这样 HC 与 Server 中 System.out 和 System.err 流须要使用 PC 中的相应流。所以 PC 的还有一个左右是输>出 PC 和 Server 中输出流打印输出的信息。domain 模式启动完毕后会启动例如以下线程用来完毕此任务:

"stdout for Host Controller" prio=10 tid=0x7fc3a400 nid=0x770e runnable [0x803fc000]
"stderr for Host Controller" prio=10 tid=0x7fc36000 nid=0x770d runnable [0x8087d000]
"stdout for Server:server-one" prio=10 tid=0x83209c00 nid=0x774f runnable [0x7ff5c000]
"stderr for Server:server-one" prio=10 tid=0x83208400 nid=0x774e runnable [0x7ffad000]

HC 用来管理 Server,Domain 模式的设计的一个目的就是在一台物理服务器上通过端口偏移执行多个 Server,HC 设计用来管理这些 Server。Domain 模式的设计的还有一
个目的是多个物理机器上的多个 Server 能够被统一管理,HC 也能够管理多个物理机器上的多个机器,这样的情况下我们称 HC 为 DC。关于怎样配置 DC 不属于此处讨论>的范畴。


Server 是指 JBoss 服务器平台,为 JEE 应用提供容器。


关闭 Server

本部分演示通过 OS 信号关闭 Server 时 PC 和 HC 相关的 Action。

我们通过例如以下步骤演示:

  • 以 domain 模式启动JBoss
  • 使用 `kill -9 PID` 关闭 Server

观察日志。我们看到例如以下日志输出:

21:39:08,800 INFO  [org.jboss.as.process.Server:server-one.status] (reaper for Server:server-one) JBAS012010: Process ‘Server:server-one‘ finished with an exit status of 137
[Host Controller] 21:39:08,801 INFO  [org.jboss.as.host.controller] (ProcessControllerConnection-thread - 2) JBAS010926: Unregistering server server-one
[Host Controller] 21:39:08,806 INFO  [org.jboss.as.host.controller] (Remoting "localhost.localdomain:MANAGEMENT" read-1) JBAS010926: Unregistering server server-one

PC 收到关闭 Server 的信息,HC Unregistering Server,例如以下为 `reaper for Server:server-one` 线程的代码:

        public void run() {
            final Process process;
            synchronized (lock) {
                process = ManagedProcess.this.process;
            }
            int exitCode;
            for (;;) try {
                exitCode = process.waitFor();
                log.processFinished(processName, Integer.valueOf(exitCode));
                break;
            } catch (InterruptedException e) {
                // ignore
            }
            boolean respawn = false;
            boolean slowRespawn = false;
            boolean unlimitedRespawn = false;
            int respawnCount = 0;
            synchronized (lock) {

                final long endTime = System.currentTimeMillis();
                processController.processStopped(processName, endTime - startTime);

分析代码。 `reaper for Server:server-one` 线程堵塞于 exitCode = process.waitFor() 行代码,等待关闭信号,且接收到关闭信号后首先日志输出。processController.processStopped(processName, endTime - startTime) 行通知 HC Unregistering Server。

深入理解 JBoss 7/WildFly Domain 模式启动过程