加载原理深度剖析[二]
在 Ambari 的服务集成体系中,metainfo.xml
的解析是服务自动发现、组件注册与运维编排的第一步。下面结合源码,系统梳理 Ambari
是如何完成 metainfo.xml 的加载与结构化解析的。
# 2. Ambari 如何解析 metainfo.xml
?
Ambari 的 Stack 管理模块,主要依赖 StackManager 和 StackModule 两大核心类。它们将 Stack 目录下的元数据、服务与组件信息,逐步解析为 Java 对象,贯穿服务的整个生命周期管理。
# 2.1 核心解析流程 🛠️
Stack 解析的主入口是 StackManager
构造方法。它负责递归遍历 Stack 根目录,依次解析服务、组件及其依赖。
@AssistedInject
public StackManager(@Assisted("stackRoot") File stackRoot,
@Assisted("commonServicesRoot") @Nullable File commonServicesRoot,
@Assisted("extensionRoot") @Nullable File extensionRoot,
@Assisted OsFamily osFamily, @Assisted boolean validate,
MetainfoDAO metaInfoDAO, ActionMetadata actionMetadata, StackDAO stackDao,
ExtensionDAO extensionDao, ExtensionLinkDAO linkDao, AmbariManagementHelper helper)
throws AmbariException {
LOG.info("Initializing the stack manager...");
if (validate) {
validateStackDirectory(stackRoot);
validateCommonServicesDirectory(commonServicesRoot);
validateExtensionDirectory(extensionRoot);
}
stackMap = new TreeMap<>();
stackContext = new StackContext(metaInfoDAO, actionMetadata, osFamily);
extensionMap = new HashMap<>();
this.helper = helper;
// 初始化并解析目录-------核心中的核心 请往这里看👀
// 初始化并解析目录-------核心中的核心 请往这里看👀
parseDirectories(stackRoot, commonServicesRoot, extensionRoot);
// 初始化并解析目录-------核心中的核心 请往这里看👀
// 初始化并解析目录-------核心中的核心 请往这里看👀
//Read the extension links from the DB
for (StackModule module : stackModules.values()) {
StackInfo stack = module.getModuleInfo();
List<ExtensionLinkEntity> entities = linkDao.findByStack(stack.getName(), stack.getVersion());
for (ExtensionLinkEntity entity : entities) {
String name = entity.getExtension().getExtensionName();
String version = entity.getExtension().getExtensionVersion();
String key = name + StackManager.PATH_DELIMITER + version;
ExtensionModule extensionModule = extensionModules.get(key);
if (extensionModule != null) {
LOG.info("Adding extension to stack/version: " + stack.getName() + "/" + stack.getVersion() +
" extension/version: " + name + "/" + version);
//Add the extension to the stack
module.getExtensionModules().put(key, extensionModule);
}
}
}
fullyResolveCommonServices(stackModules, commonServiceModules, extensionModules);
fullyResolveExtensions(stackModules, commonServiceModules, extensionModules);
fullyResolveStacks(stackModules, commonServiceModules, extensionModules);
populateDB(stackDao, extensionDao);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
构造函数中 parseDirectories()
方法递归解析 Stack、Common Services 和 Extension
目录,逐步加载关键点所有 metainfo.xml
文件。
# parseDirectories() 方法
protected void parseDirectories(File stackRoot, File commonServicesRoot, File extensionRoot) throws AmbariException {
// 解析通用服务目录
commonServiceModules = parseCommonServicesDirectory(commonServicesRoot);
// 解析堆栈目录-------核心中的核心 请往这里看👀
// 解析堆栈目录-------核心中的核心 请往这里看👀
stackModules = parseStackDirectory(stackRoot);
// 解析堆栈目录-------核心中的核心 请往这里看👀
// 解析堆栈目录-------核心中的核心 请往这里看👀
LOG.info("About to parse extension directories");
// 解析扩展目录
extensionModules = parseExtensionDirectory(extensionRoot);
}
2
3
4
5
6
7
8
9
10
11
12
其中 parseStackDirectory(stackRoot)
是核心,递归查找每个 Stack 版本目录,加载 metainfo.xml 并注册服务:
private Map<String, StackModule> parseStackDirectory(File stackRoot) throws AmbariException {
Map<String, StackModule> stackModules = new HashMap<>();
File[] stackFiles = stackRoot.listFiles(StackDirectory.FILENAME_FILTER);
for (File stack : stackFiles) {
if (stack.isFile()) {
continue;
}
for (File stackFolder : stack.listFiles(StackDirectory.FILENAME_FILTER)) {
if (stackFolder.isFile()) {
continue;
}
String stackName = stackFolder.getParentFile().getName();
String stackVersion = stackFolder.getName();
// 解析堆栈目录-------核心中的核心 请往这里看👀
// 解析堆栈目录-------核心中的核心 请往这里看👀
StackModule stackModule = new StackModule(new StackDirectory(stackFolder.getPath()), stackContext);
// 解析堆栈目录-------核心中的核心 请往这里看👀
// 解析堆栈目录-------核心中的核心 请往这里看👀
String stackKey = stackName + StackManager.PATH_DELIMITER + stackVersion;
stackModules.put(stackKey, stackModule);
stackMap.put(stackKey, stackModule.getModuleInfo());
}
}
if (stackMap.isEmpty()) {
throw new AmbariException("Unable to find stack definitions under stackRoot = " + stackRoot.getAbsolutePath());
}
return stackModules;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
笔记
每一个 Stack 目录下都会实例化一个 StackModule,对应于每个 metainfo.xml 文件的解析对象。
# 2.2 StackModule
类中的初始化 🧩
每个 StackModule
代表一个 Stack 版本,它的初始化会自动加载并解析 metainfo.xml
:
public StackModule(StackDirectory stackDirectory, StackContext stackContext) {
this.stackDirectory = stackDirectory;
this.stackContext = stackContext;
stackInfo = new StackInfo();
populateStackInfo(); // 解析 metainfo.xml
}
2
3
4
5
6
# populateStackInfo() 方法
核心逻辑就是通过 stackDirectory.getMetaInfoFile()
获取 metainfo.xml
的 XML 映射对象,再转化为 StackInfo:
StackMetainfoXml smx = stackDirectory.getMetaInfoFile();
if (smx != null) {
if (!smx.isValid()) {
stackInfo.setValid(false);
stackInfo.addErrors(smx.getErrors());
}
stackInfo.setMinJdk(smx.getMinJdk());
stackInfo.setMaxJdk(smx.getMaxJdk());
stackInfo.setActive(smx.getVersion().isActive());
stackInfo.setParentStackVersion(smx.getExtends());
}
2
3
4
5
6
7
8
9
10
11
提示
这一步会把 metainfo.xml 的 JDK 依赖、Stack 继承、启用状态等基础信息都映射到 StackInfo 实例中。
# 2.3 服务与组件的逐层解析 🔍
# populateServices() 方法
服务和组件的注册与解析,是通过遍历 Service 目录、调用 populateService()
方法实现的:
private void populateServices() throws AmbariException {
for (ServiceDirectory serviceDir : stackDirectory.getServiceDirectories()) {
populateService(serviceDir); // 调用子服务解析
}
}
2
3
4
5
# populateService() 详细逻辑
populateService()
用于把 metainfo.xml 中的每个服务,注册为 ServiceModule 对象:
private void populateService(ServiceDirectory serviceDirectory) {
Collection<ServiceModule> serviceModules = new ArrayList<>();
// unfortunately, we allow multiple services to be specified in the same metainfo.xml,
// so we can't move the unmarshal logic into ServiceModule
ServiceMetainfoXml metaInfoXml = serviceDirectory.getMetaInfoFile();
if (!metaInfoXml.isValid()) {
stackInfo.setValid(metaInfoXml.isValid());
setValid(metaInfoXml.isValid());
stackInfo.addErrors(metaInfoXml.getErrors());
addErrors(metaInfoXml.getErrors());
return;
}
List<ServiceInfo> serviceInfos = metaInfoXml.getServices();
for (ServiceInfo serviceInfo : serviceInfos) {
// 初始化并解析Service-------核心中的核心 请往这里看👀
// 初始化并解析Service-------核心中的核心 请往这里看👀
ServiceModule serviceModule = new ServiceModule(stackContext, serviceInfo, serviceDirectory);
// 初始化并解析Service-------核心中的核心 请往这里看👀
// 初始化并解析Service-------核心中的核心 请往这里看👀
serviceModules.add(serviceModule);
if (!serviceModule.isValid()) {
stackInfo.setValid(false);
setValid(false);
stackInfo.addErrors(serviceModule.getErrors());
addErrors(serviceModule.getErrors());
}
}
addServices(serviceModules);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
提示
每个 ServiceModule 都包含了该服务的所有组件(Master/Slave/Client)、脚本、配置文件、健康检查等信息。