AS7内部架构概述(AS 7 Internal Architecture Overview)

简单点说,AS7由两个主要组件组成:

  • 一个核心的易管理的服务容器(MSC):基于模块化类加载实现

  • 基于MSC实现的扩展机制,通过这种扩展机制来实现很多用户使用的应用服务器功能:请求处理 、事务管理等

AS 发布版本提供两种客户端管理接口(CLI和web管理控制台)

AS7核心

AS核心由以下几个主要元素组成:

  • 模块加载系统:jboss-modules库

  • 更快速,易扩展的服务容器框架:jboss-msc库

  • 可扩展的管理层:通个这个管理层调用MSC实现服务的添加、删除、修改。此管理层还为AS提供一致的持久的配置模型;管理层包括以下内容:

    • 核心元素:jboss-dmr库以及AS 自己代码库中的controller、controller-client、deployment-repository、domain-management、network模块以及server模块中的部分模块

    • 远程管理能力:通过protocol和domain-http模块实现

    • 域多服务器管理:通过process-controller、host-controller模块实现

    • 其它元素:managment-client-content、platform-mbean  

  • 实时处理部署内容安装的部署框架,这个功能通过server模块提供

AS7扩展

终端用户所使用的应用服务器功能都是通过扩展方式提供的。AS7基础代码中大部分的模块都是通过扩展实现。这些扩展中的大部分实现了java EE的某些规范

某个扩展模块通过实现一个接口(org.jboss.as.controller.Extension)来与AS的核心管理层整合。通过这个整合,扩展可以:

  • 参与配置文件的解析和编排

  • 通过AS的管理api来注册资源和暴露操作

  • 把服务安装进AS服务容器中(MSC)

  • 在AS的部署框架中注册部署单元处理器

    通过https://docs.jboss.org/author/display/AS72/Extending+JBoss+AS+7可以查看更多扩展实现的内容

    通过https://community.jboss.org/wiki/ExtendingAS7可以了解扩展实现机制与以前版本的实现的不同之处

AS启动过程

了解AS7架构的最好方式就是跟踪下standalone模式下服务器的启动过程

启动的本质

当执行bin/standalone.sh的时候,实际是执行了下面的命令:

java -jar jboss-modules.jar -mp modules org.jboss.as.standalone 

分解说明:

java -jar jboss-modules.jar

从执行命令看,启动时VM调用的入口不是JBOSS AS类库而是jboss-modules.jar(实际是org.jboss.modules.Main class),所以第一步是

设置模块类加载环境。

-mp modules

上面这个时候作为参数传递给org.jboss.modules.Main.main()。-mp是“module path”的缩写,它的值类似于$PATH环境变量。

通过在这个设置的环境里面找到模块名为org.jboss.as.standalone的模块,这个模块将会被jboss-modules加载;在这个模块的目录下

有一个模块文件:/org/jboss/as/standalone/main/module.xml,这个文件中定义了模块的入口类:

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

通过这个配置,jboss-modules模块就知道调用哪个类来启动这个模块,并且把参数传递给main()函数;

这个函数里面做了很多事情,但是有两件事情可以帮助理解AS的开发:

  • 任何还没被使用的命令行参数都会被处理,并且会创建类ServerEnvironment的一个实例对象。这个对象封装了通过命令行或者系统的属性信息。这些信息包括:服务器实例的根路径是什么,配置文件的路径等;然后,这些信息可以通过调用服务ServerEnvironmentService被任何其它服务获取

  • 实现Bootstrap接口(实际是org.jboss.as.server.BootstrapImpl.class)的对象会被创建和配置,并且它的bootstrap()方法会被调用

服务的启动(Service-based Boot)

BootstrapImpl的bootstrap方法做得最重要的事情是创建一个ServiceContainer接口实现的实例对象(由MSC库提供)。通过这个容器来管理所有运行中的服务。一个服务实现了服务接口Service,里面有start和stop两个方法,通过这两个方法来实现服务的启动和停止(个人理解:服务容器可以理解为osgi的容器或者spring容器,服务也可以理解为osgi里面的服务,通过接口定义来实现对服务bean对象的管理)。这个服务接口还暴露了一个方法:public T getValue(),这个方法中的T代表实际的服务对象类型(即通过这个对象可以调用具体的服务方法,实现service接口的类更多是容器需要通过它来对服务组件进行注册,实际的服务暴露是通过其它对象提供,比如在安全子系统中:SecurityDomainService implements Service<SecurityDomainContext>是服务注册类,但是服务提供类是SecurityDomainContext )

ServiceContainer提供管理服务的api--配置安装服务,移除服务,触发启动和停止,查找已经存在的服务。配置服务是指服务之间的依赖,当服务依赖于其它服务时,在启动此服务之前需要服务容器注入被依赖的其它服务的服务对象(getValue返回值)。如果一个服务依赖于别的服务,在启动这个服务的时候要求被依赖的服务是启动成功的。如果因为某些原因被依赖的服务需要停止,则先调用当前服务的stop方法

ServiceContainer内部包含一个线程池。通过线程池来执行管理服务的任务,保障服务管理的高性能表现。例如:执行某个服务的start方法就是任务,通过ServiceContainer的线程池中的线程来执行。由于这个原因,需要注意,任何跟服务启动和停止的相关动作都是基于多线程执行的,比如:通知服务容器安装服务的线程与执行服务的start方法的线程并不是同一个线程,跟执行stop方法的线程也不是同一个线程(如果理解成同一个线程,可能会导致变量的值获取不到,比如在启动的时候设置一个线程内部的变量,在关闭的时候获取等,另外,每个线程独立执行,需要按照线程间同步技术来保证线程执行的先后顺序)

从上可知,在执行到BootstrapImpl.bootstrap()之前,所有动作都是在JVM的main线程里面执行的(单一线程),在bootstrap里面,则采用线程池的多线程来执行某些动作任务。

BootstrapImpl.bootstrap()安装了两个服务:

  • ControlledProcessStateService:通过这个服务可以访问一个枚举对象的值,它包含当前服务器(server)的运行状态(STARTING, STOPPING, RUNNING, RESTART_REQUIRED, RELOAD_REQUIRED),这个服务是唯一一个一旦启动就不能关闭的服务,除非整个VM都被停止(也就是jboss停止)

  • ApplicationServerService:这是应用服务器(server)的根服务,其它服务以某种方式或其它形式都会依赖于这个服务。当通过命令行执行reload操作的时候,服务器处理这个操作的方式是通知服务容器(MSC)停止这个服务(ApplicationServerService),因为其它服务依赖于这个服务,所以相当于把所有服务都停止了,然后服务容器再启动服务。

ApplicationServerService在它的start方法里面启动了很多服务,其中最重要的服务是ServerService

The ServerService and the Controller Boot Thread

ServerService主要完成两个职责:可扩展管理层以及部署框架(AS核心四点功能的第三和第四)。就像"Extending JBoss AS 7" 文档中描述的那样,ServerService的主要职责与扩展实现类中initialize(ExtensionContext context)方法的实现一样,对于核心管理模型来讲,主要实现以下两个工作:

  • 注册资源定义,属性定义以及核心资源管理器OperationStepHandlers

  • 组册部署处理器DeploymentUnitProcessors


ServerService 实现接口Service<ModelController>,ModelController是被管理的AS程序(例如一个server或者被管理域中的HostController)的管理操作的处理中心。

在ServerService的start方法里面,单独创建了一个叫做“Controller Boot Thread”的线程,通过这个线程来协调处理剩下的启动过程,它主要做了以下这些任务:

  • 触发server配置文件的解析(例如standalone.xml),通过解析形成一个管理操作列表,管理操作列表中的管理操作将会被ModelController执行,从而达到以配置文件的内容在线更新服务器运行时配置的目的。

  • 把管理操作列表中的操作传递给ModelController的execute方法。然后,ModelController使用创建的单独线程(Controller Boot Thread)来执行这些管理操作任务。

  • Blocks until all services added into the runtime as part of handling those management ops have either started or failed to start

  • 记录启动完成日志


XML Parsing, Extension Loading, Extension Parsing Initialization

负责解析AS7配置文档(standone.xml,domain.xml,host.xml)的解析器的任务是形成一个管理操作列表,这些管理操作将会被ModelController(ServerService服务中的getValue返回值)执行,从而完成运行时配置项的更新(即以当前配置文件的内容更新运行时程序内部的配置)。

表达每个操作的内容的形式是一样的(就是CLI中的操作名 操作参数的组织形式)

当解析配置文件中的<extension>元素时,所有解析器都会有一些相同的行为:

  • extension的name属性就是模块的名字,这个模块包含了接口org.jboss.as.controller.Extension的实现(eap和as7.1.1版本中,没有name属性,变为module属性)

  • 一旦模块名被解析出来,解析器就会要求JBoss Modules加载这个模块

  • 模块被加载完后,java.lang.ServiceLoader加载机制就会去加载模块的实现(实现Extension接口的类)(把扩展的模块注册成服务)

  • 模块中Extension接口实现类的方法initializeParsers(ExtensionParsingContext context)就会被调用。在这个方法中去注册这个扩展子系统的XML解析器,解析子系统标签subsystem(s)下此模块的配置。

  • 当AS核心XML解析器解析到<subsystem>元素时,根据extension命名空间的名字来确定此部分的解析器,这个解析器在上一步骤中注册完成,然后调用此注册的解析器完成此部分内容的解析


Execution of Boot Management Operations, Extension Initialization

执行管理操作,初始化扩展模块

一旦XML解析完成,会形成一个即将被ModelController执行的管理操作列表。这些管理操作拥有相同的格式(地址,操作名,参数),并且与启动后通过CLI发送的操作请求的格式是一样的。控制器启动线程(Controller Boot Thread)要求ModelController把这些操作当作一个整体单元执行,每一个操作是整体单元中的一步。整个工作单元的操作执行分为三个阶段,当前一阶段执行完成,后一阶段才能开始执行(This execution pattern applies whenever any operation or atomic list of operations is invoked, not just during boot)

  • MODEL阶段--为每个操作注册(准备)的OperationStepHandler必须更新进AS服务器的内部配置模型(即必须让AS配置模型知道此操作的处理器是什么)。如果有需要,在RUNTIME阶段还可以为同一个操作再注册一个处理器(handler);//这个handler应该是这个操作具体执行的代码

  • RUNTIME阶段--在MODEL阶段为操作注册的处理器OperationStepHandler调用JBoss MSC 容器ServiceContainer,执行安装/移除/更新此模块相关的服务。ServiceContainer通过自己的线程池来完成启动和停止服务的任务;调用OperationStepHandler的线程并不直接完成服务的启动和停止这些任务,而是通过ServiceContainer内的线程池异步执行(The thread executing the OperationStepHandler does not do this directly, and the handler implementation needs to recognize that service start/stop will be asynchronous),在实现OperationStepHandler时要注意这种start/stop方法的异步机制原理

  • VERIFY阶段--MODEL或者RUNTIME处理器(handler)可以注册一个VERIFY阶段的处理器,当ServiceContainer在RUNTIME阶段完成所有服务的变更的时候,VERIFY处理器用于检查运行时服务的更改是否成功(即在MODEL或者RUNTIME阶段通过处理器把配置文件信息更新进内存中的运行对象,通过验证阶段的处理器去验证所有的这些更新是否成功)

当启动操作列表被传给ModelController之前,将会检查这些操作是否是添加扩展资源的操作(比如:/extension=org.foo.extension:add,这是一个CLI语法的操作表示),一旦发现某个操作时添加扩展资源的,则这个操作相关的扩展接口实现类的方法initialize(ExtensionContext context)将会被调用。这种机制给Extension提供了一个机会:在启动操作执行前,向AS核心管理层注册它自己的资源 、属性定义以及OperationStepHandlers。

Wait for Service Container Stability and Boot Completion

等待服务容器稳定和启动完成

AS7启动的最后一个步骤是等待MSC服务容器安装和启动所有被处理器(OperationStepHandler)添加的服务。就如上面所描述的那样,这些服务的安装和执行是通过ServiceContainer的内部线程池执行的,服务不是被控制启动的线程启动的。每个被启动的服务都会在控制器对象绑定一个监听器(暂时不确定此控制器对象是那个,应该是SC启动服务完成后通过监听器告诉控制对象),通过这个控制对象的监听机制,ServerService可以追踪服务的启动状态。启动控制线程通过这个监听机制来确保ServiceContainer已经启动稳定,这就是说所有服务要么启动成功要么失败,然后启动过程完成。然后,控制器启动线程把ControlledProcessState的状态从STARING修改成RUNNING,然后写一条日志表明启动完成。

Deployment Processing

部署过程

当需要部署某些内容时,AS核心管理层的一些管理操作将会被调用。处理部署工作的逻辑层会提取操作请求的参数(比如部署包名),然后安装需要部署的服务进入ServiceContainer:

  • Service<VirtualFile>接口的实现类提供一个org.jboss.vfs.VirtualFile类的对象代表被部署的内容

  • Service<DeploymentUnit>的实现类(在RootDeploymentUnitService情况下才会用到这个)提供一个DeploymentUnit代表一个部署。一个DeploymentUnit包含了一个部署的生命周期数据(被持久化),DeploymentUnit会在以后传递给各种DeploymentUnitProcessor的实现类,这个实现类通过各种动作来完成部署需要的运行时服务安装

所有DeploymentUnitProcessor(简称DUP)的实现类都会被注入一个RootDeploymentUnitService的引用,这个DeploymentUnitProcessor会在启动时被ServerService注册或者在子系统中被注册。DUP的实现类按部署过程的阶段分成不同的组(不同的阶段为一组),在某个阶段,这些DUP将会被排序,然后执行。所有的DUP将会被组织成一个链条,每个DUP将承担一部分部署的任务,通过所有的DUP的执行,完成从二进制文件到服务的部署。


整个部署过程分为 不同的阶段,可以查看枚举类型Phase知道都有哪些阶段(STRUCTURE,PARSE,REGISTER,DEPENDENCIES,FIRST_MODULE_USE,POST_MODULE,INSTALL,CLEANUP,其代码位置为:\jboss-eap-6.2-src\server\src\main\java\org\jboss\as\server\deployment\Phase.java);通过一个叫做DeploymentUnitPhaseService的服务代表每个阶段,并且把此服务组册进入服务容器(ServiceContainer);每个阶段的服务都依赖于前一阶段的服务,当前阶段的服务在它的start方法中组册下一阶段的服务。RootDeploymentUnitService在其start方法中组册第一个阶段的服务(STRUCTURE),第一阶段的服务依赖于RootDeploymentUnitService服务。这样实现的效果是,如果RootDeploymentUnitService被停止了,第一阶段的服务会被停止,接着第二第三及以后所有阶段的服务也会被停止。

各个阶段的服务的主要工作是在它们的start和stop方法里面调用当前阶段的DeploymentUnitProcessor的deploy和undeploy方法。在start方法里面调用deploy,在stop方法里面调用undeploy。每次deploy或者undepoy都会一个DeploymentPhaseContext对象,通过它提供的上下文key实现对服务容器的访问,从而实现服务的安装和卸载


Deployment Processing and Modules

部署过程和部署模块

DeploymentUnitProcessors的其中一个任务就是为当前部署设置模块加载环境。每个顶层的部署都会有一个动态产生的模块。对于包含已知子部署模块的部署类型(比如ear中包含wars ejb jars),它的子部署类型同样会自动生成一个模块。至于部署模块所明显依赖的其它模块,需要部署框架来决定都依赖那些模块,通过分析部署内容来获取依赖(解析部署描述符 manifests文件,或者扫描注解)

以上这段描述的核心是:DUP要负责设置类加载环境,这涉及到模块的依赖,对于像EAR这种部署包,它的依赖会先检查里面的war包以及jar包,对于其它的部署包,比如war或者普通的jar,其加载的时候,还会解析他们所依赖的模块有哪些,然后加载所需要的模块的类。

部署过程中,有一个比较关键的类org.jboss.as.server.deployment.module.ModuleSpecProcessor,它本身是一个DeploymentUnitProcessor的子类.DeploymentUnitProcessor会配置和安装一个MSC服务,这个服务会和jboss-modules模块交互来动态产生一个部署模块。ModuleSpecProcessor之前执行的那些DeploymentUnitProcessors会分析部署的内容并且添加上下文信息到DeploymentUnit,通过这个DeploymentUnit,ModuleSpecProcessor可以确定此部署可以访问到的模块有哪些。比如:JPA子系统注册的一个DUP,经过分析,得出此子系统需要JPA支持(比如检测persistence.xml文件是否存在),并且会记录这些被依赖的JPA模块。


参考资料:

     英文原文地址:https://developer.jboss.org/wiki/AS7InternalArchitectureOverview

你可能感兴趣的:(jboss,架构,启动过程,msc,as7)