简单点说,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