SOFAArk容器通过监听springboot启动事件来启动自己,启动类SofaArkBootstrap是由AppClassLoader加载的,
然后会创建一个新的ContainerClassLoader来加载容器相关的类。
容器启动过程中会为每一个plugin和biz模块都创建一个对应的PluginClassLoader和BizClassLoader,每一个plugin都有一个独立的PluginClassLoader加载,biz模块也一样。这样就做到了类隔离。
SOFAArk容器中,一个ContainerClassLoader,N个PluginClassLoader和BizClassLoader。
1 官方流程
官方流程:https://www.sofastack.tech/projects/sofa-boot/sofa-ark-startup/
Ark 应用的整体启动流程如下图所述:
当用 java -jar 启动 Ark 包 或者 在 IDE 中通过 SofaArkBootstrap.launch 启动 Ark 应用时,相应 Launcher 入口会负责启动应用,其中会反射调用 ArkContainer 的入口,初始化 ArkService ,然后依次执行 pipeline,来完成整个 Ark 应用的启动。
ArkService
Ark Serivce 是 Ark 容器中的服务,底层使用 Guice 对服务进行管理。同时针对服务,提供了生命周期接口 com.alipay.sofa.ark.spi.service.ArkService
publicinterfaceArkService {
/*
Ark Service init
@throws ArkException
/
voidinit()throws ArkException;
/*
Ark Service dispose
@throws ArkException
/
voiddispose()throws ArkException;
}
当服务实现了上述接口时,在 Ark Serivce 容器启动时和停止时会调用相应的生命周期接口
Pipeline 服务
Pipeline 也是注册在 Ark Service 容器中的一个服务,服务本身是没有顺序和优先级的,在 Pipeline 中会对服务进行一些组装,同时完成整个 Ark 容器的启动
Archive 解析
在 Pipeline 的最开始,会将运行的 fatjar 进行解析,解析成运行时需要的模型,主要包括 Ark 插件模型和 Ark 业务模型,并将这些模型注册到 Ark Service 中的 PluginManagerService 以及 BizManagerService 中
初始化环境
设置一些运行时需要的默认参数,比如设置 log4j.ignoreTCL 为 true 让 log4j/log4j2 初始化是日志不要从 ThreadContextClassloader 中寻找配置文件(背景)
注册容器服务
在 Ark 容器中会发布一些服务供其它的插件来使用,比如 BizDeployer 来让 SOFAArk 官方插件 sofa-jarslink 来完成 biz 的动态加载/卸载等
部署 Ark 插件
从 PluginManagerService 中获取到所有的 Ark 插件,并按照插件优先级顺序: ClassloaderService 准备插件 export 类的 map 映射 PluginDeployService 启动插件的 com.alipay.sofa.ark.spi.service.PluginActivator
启动 Ark 业务
从 BizManagerService 中获取到所有的 Ark 业务,并执行业务配置在 MANIFEST.MF 属性 Main-Class 中提供的入口 main 函数
2 springboot启动流程:
1 com.alipay.sofa.ark.springboot.listener.ArkApplicationStartListener 监听spring启动事件
@Override
publicvoid onApplicationEvent(SpringApplicationEvent event) {
try {
if (isSpringBoot2()
&& APPLICATION_STARTING_EVENT.equals(event.getClass().getCanonicalName())) {
startUpArk(event);
}
if (isSpringBoot1()
&& APPLICATION_STARTED_EVENT.equals(event.getClass().getCanonicalName())) {
startUpArk(event);
}
} catch (Throwable e) {
thrownew RuntimeException(“Meet exception when determine whether to start SOFAArk!”, e);
}
}
publicvoid startUpArk(SpringApplicationEvent event) {
if (LAUNCH_CLASSLOADER_NAME.equals(this.getClass().getClassLoader().getClass().getName())) {
SofaArkBootstrap.launch(event.getArgs());
}
}
3 利用SofaArkBootstrap启动ark容器);)
SofaArkBootstrap.launch(event.getArgs()););)
publicstaticvoid launch(String[] args) {
try {
if (!isSofaArkStarted()) {
entryMethod = new EntryMethod(Thread.currentThread());
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(
entryMethod.getDeclaringClassName());
LaunchRunner launchRunner = new LaunchRunner(SofaArkBootstrap.class.getName(),
MAIN_ENTRY_NAME, args);
Thread launchThread = new Thread(threadGroup, launchRunner,
entryMethod.getMethodName());
launchThread.start();
LaunchRunner.join(threadGroup);
threadGroup.rethrowUncaughtException();
System.exit(0);
}
} catch (Throwable e) {
thrownew RuntimeException(e);
}
}
核心过程launchRunner的run 当前这个类LaunchRunner由AppClassLoader加载生成。 反射调用SofaArkBootstrap.remain方法
publicvoid run() {
Thread thread = Thread.currentThread();
ClassLoader classLoader = thread.getContextClassLoader();
try {
Class<?> startClass = classLoader.loadClass(this.startClassName);
Method entryMethod;
try {
entryMethod = startClass.getMethod(startMethodName, String[].class);
} catch (NoSuchMethodException ex) {
entryMethod = startClass.getDeclaredMethod(startMethodName, String[].class);
}
if (!entryMethod.isAccessible()) {
entryMethod.setAccessible(true);
}
entryMethod.invoke(null, new Object[] { this.args });
} catch (NoSuchMethodException ex) {
Exception wrappedEx = new Exception(
String.format(
“The specified entry class:%s doesn’t contain an entry method:%s with appropriate signature.”,
this.startClassName, this.startMethodName), ex);
thread.getThreadGroup().uncaughtException(thread, wrappedEx);
} catch (Throwable ex) {
thread.getThreadGroup().uncaughtException(thread, ex);
}
}
com.alipay.sofa.ark.support.startup.SofaArkBootstrap#remain
privatestaticvoid remain(String[] args) throws Exception {// NOPMD
AssertUtils.assertNotNull(entryMethod, “No Entry Method Found.”);
URL[] urls = getURLClassPath();
new ClasspathLauncher(new ClassPathArchive(entryMethod.getDeclaringClassName(),
entryMethod.getMethodName(), urls)).launch(args, getClasspath(urls),
entryMethod.getMethod());
}
launch
public Object launch(String[] args, String classpath, Method method) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = createContainerClassLoader(getContainerArchive());
List
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_CONTAINER_ARGUMENTS_MARK,
CommandArgument.CLASSPATH_ARGUMENT_KEY, classpath));
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_BIZ_ARGUMENTS_MARK,
CommandArgument.ENTRY_CLASS_NAME_ARGUMENT_KEY, method.getDeclaringClass().getName()));
attachArgs.add(String.format(“%s%s=%s”, CommandArgument.ARK_BIZ_ARGUMENTS_MARK,
CommandArgument.ENTRY_METHOD_NAME_ARGUMENT_KEY, method.getName()));
attachArgs.addAll(Arrays.asList(args));
return launch(attachArgs.toArray(new String[attachArgs.size()]), getMainClass(),
classLoader);
}
1 创建了一个ContainerClassLoader类加载器(ContainerClassLoader),后面会用来加载SOFAArk相关的类
2 ark的,命令行参数 解析出来
3 再一次调用 launch方法,最终createMainMethodRunner方法返回的是MainMethodRunner这个工具类的实例,MainMethodRunner只是一个执行器,它实际是为了执行ArkContainer类的main方法。
com.alipay.sofa.ark.container.ArkContainer#main方法
try {
LaunchCommand launchCommand = LaunchCommand.parse(args);
if (launchCommand.isExecutedByCommandLine()) {
ExecutableArkBizJar executableArchive;
File rootFile = new File(URLDecoder.decode(launchCommand.getExecutableArkBizJar()
.getFile()));
if (rootFile.isDirectory()) {
executableArchive = new ExecutableArkBizJar(new ExplodedArchive(rootFile));
} else {
executableArchive = new ExecutableArkBizJar(new JarFileArchive(rootFile,
launchCommand.getExecutableArkBizJar()));
}
return new ArkContainer(executableArchive, launchCommand).start();//最主要流程
} else {
ClassPathArchive classPathArchive = new ClassPathArchive(
launchCommand.getEntryClassName(), launchCommand.getEntryMethodName(),
launchCommand.getClasspath());
returnnew ArkContainer(classPathArchive, launchCommand).start();
}
} catch (IOException e) {
thrownew ArkRuntimeException(String.format("SOFAArk startup failed, commandline=%s",
LaunchCommand.toString(args)), e);
}
com.alipay.sofa.ark.container.ArkContainer#start
public Object start() throws ArkRuntimeException {
AssertUtils.assertNotNull(arkServiceContainer, "arkServiceContainer is null !");
if (started.compareAndSet(false, true)) {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
publicvoid run() {
stop();
}
}));
// 1 bootstrap.properties配置文件
prepareArkConfig();
// 2 初始化log相关配置
reInitializeArkLogger();
// 3 启动Ark Service Container
arkServiceContainer.start();
// 4 注冊的Pipeline
Pipeline pipeline = arkServiceContainer.getService(Pipeline.class);
// 5
pipeline.process(pipelineContext);
System.out.println("Ark container started in " + (System.currentTimeMillis() - start) //NOPMD
+ " ms.");
}
returnthis;
}
启动Ark Service Container
com.alipay.sofa.ark.container.service.ArkServiceContainer#start
ClassLoader oldClassLoader = ClassLoaderUtils.pushContextClassLoader(getClass()
.getClassLoader());
try {
LOGGER.info("Begin to start ArkServiceContainer");
injector = Guice.createInjector(findServiceModules());
for (Binding<ArkService> binding : injector
.findBindingsByType(new TypeLiteral<ArkService>() {
})) {
arkServiceList.add(binding.getProvider().get());
}
Collections.sort(arkServiceList, new OrderComparator());
for (ArkService arkService : arkServiceList) {
LOGGER.info(String.format("Init Service: %s", arkService.getClass().getName()));
arkService.init();
}
ArkServiceContainerHolder.setContainer(this);
ArkClient.setBizFactoryService(getService(BizFactoryService.class));
ArkClient.setBizManagerService(getService(BizManagerService.class));
ArkClient.setInjectionService(getService(InjectionService.class));
ArkClient.setEventAdminService(getService(EventAdminService.class));
ArkClient.setArguments(arguments);
LOGGER.info("Finish to start ArkServiceContainer");
} finally {
ClassLoaderUtils.popContextClassLoader(oldClassLoader);
}
}
spi 注册ClassLoaderServiceImpl
protectedvoid configure() {
binder().bind(Pipeline.class).to(StandardPipeline.class);
Multibinder<ArkService> arkServiceMultibinder = Multibinder.newSetBinder(binder(),
ArkService.class);
arkServiceMultibinder.addBinding().to(PluginDeployServiceImpl.class);
arkServiceMultibinder.addBinding().to(BizDeployServiceImpl.class);
arkServiceMultibinder.addBinding().to(ClassLoaderServiceImpl.class);
arkServiceMultibinder.addBinding().to(StandardTelnetServerImpl.class);
binder().bind(PluginManagerService.class).to(PluginManagerServiceImpl.class);
binder().bind(BizManagerService.class).to(BizManagerServiceImpl.class);
binder().bind(ClassLoaderService.class).to(ClassLoaderServiceImpl.class);
binder().bind(PluginDeployService.class).to(PluginDeployServiceImpl.class);
binder().bind(BizDeployService.class).to(BizDeployServiceImpl.class);
binder().bind(RegistryService.class).to(RegistryServiceImpl.class);
binder().bind(InjectionService.class).to(InjectionServiceImpl.class);
binder().bind(TelnetServerService.class).to(StandardTelnetServerImpl.class);
binder().bind(BizFactoryService.class).to(BizFactoryServiceImpl.class);
binder().bind(PluginFactoryService.class).to(PluginFactoryServiceImpl.class);
binder().bind(ExtensionLoaderService.class).to(ExtensionLoaderServiceImpl.class);
binder().bind(EventAdminService.class).to(EventAdminServiceImpl.class);
}
// 4 注冊的Pipeline
privatevoid initializePipeline() {
addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(HandleArchiveStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(RegisterServiceStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(ExtensionLoaderStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(DeployPluginStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(DeployBizStage.class))
.addPipelineStage(
ArkServiceContainerHolder.getContainer().getService(FinishStartupStage.class));
}
HandleArchiveStage 注册plugin和biz模块, 仅注册不部署
RegisterServiceStage plugin,模块和事件这些内部服务不可被覆盖, 因此先发布
ExtensionLoaderStage 扩展服务
DeployPluginStage 部署插件
DeployBizStage 部署biz模块
FinishStartupStage 结束, 里面主要发送一个结束事件
3 主要加载过程
0 HandleArchiveStage 动态配置 注册\初始化插件(每一个插件都有一个新的PluginClassLoader类加载器)Activator
publicfinal String SOFA_ARK_CONTAINER = “SOFA-ARK/container/“;
publicfinal String SOFA_ARK_MODULE = “SOFA-ARK/biz/“;
publicfinal String SOFA_ARK_PLUGIN = “SOFA-ARK/plugin/“;
publicvoid registerPlugin(Plugin plugin) {
if (plugins.putIfAbsent(plugin.getPluginName(), plugin) != null) {
thrownew ArkRuntimeException(String.format(“duplicate plugin: %s exists.”,
plugin.getPluginName()));
}
}
public Plugin createPlugin(PluginArchive pluginArchive) throws IOException,
IllegalArgumentException {
AssertUtils.isTrue(isArkPlugin(pluginArchive), “Archive must be a ark plugin!”);
PluginModel plugin = new PluginModel();
Attributes manifestMainAttributes = pluginArchive.getManifest().getMainAttributes();
plugin
.setPluginName(manifestMainAttributes.getValue(PLUGIN_NAME_ATTRIBUTE))
.setGroupId(manifestMainAttributes.getValue(GROUP_ID_ATTRIBUTE))
.setArtifactId(manifestMainAttributes.getValue(ARTIFACT_ID_ATTRIBUTE))
.setVersion(manifestMainAttributes.getValue(PLUGIN_VERSION_ATTRIBUTE))
.setPriority(manifestMainAttributes.getValue(PRIORITY_ATTRIBUTE))
.setPluginActivator(manifestMainAttributes.getValue(ACTIVATOR_ATTRIBUTE))
.setClassPath(pluginArchive.getUrls())
.setPluginUrl(pluginArchive.getUrl())
.setExportClasses(manifestMainAttributes.getValue(EXPORT_CLASSES_ATTRIBUTE))
.setExportPackages(manifestMainAttributes.getValue(EXPORT_PACKAGES_ATTRIBUTE))
.setImportClasses(manifestMainAttributes.getValue(IMPORT_CLASSES_ATTRIBUTE))
.setImportPackages(manifestMainAttributes.getValue(IMPORT_PACKAGES_ATTRIBUTE))
.setImportResources(manifestMainAttributes.getValue(IMPORT_RESOURCES_ATTRIBUTE))
.setExportResources(manifestMainAttributes.getValue(EXPORT_RESOURCES_ATTRIBUTE))
.setPluginClassLoader(
new PluginClassLoader(plugin.getPluginName(), plugin.getClassPath()))
.setPluginContext(new PluginContextImpl(plugin));
return plugin;
导出类与类加载器关系
@Override
publicvoid prepareExportClassAndResourceCache() {
for (Plugin plugin : pluginManagerService.getPluginsInOrder()) {
for (String exportIndex : plugin.getExportPackageNodes()) {
exportNodeAndClassLoaderMap.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String exportIndex : plugin.getExportPackageStems()) {
exportStemAndClassLoaderMap.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String exportIndex : plugin.getExportClasses()) {
exportClassAndClassLoaderMap
.putIfAbsent(exportIndex, plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportResources()) {
exportResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportResourceAndClassLoaderMap.get(resource).add(plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportPrefixResourceStems()) {
exportPrefixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportPrefixStemResourceAndClassLoaderMap.get(resource).add(
plugin.getPluginClassLoader());
}
for (String resource : plugin.getExportSuffixResourceStems()) {
exportSuffixStemResourceAndClassLoaderMap.putIfAbsent(resource, new LinkedList<>());
exportSuffixStemResourceAndClassLoaderMap.get(resource).add(
plugin.getPluginClassLoader());
}
}
}
//其他插件导入
protected Class<?> resolveExportClass(String name) {
if (shouldFindExportedClass(name)) {
ClassLoader importClassLoader = classloaderService.findExportClassLoader(name);
if (importClassLoader != null) {
try {
return importClassLoader.loadClass(name);
} catch (ClassNotFoundException e) {
// just log when debug level
if (ArkLoggerFactory.getDefaultLogger().isDebugEnabled()) {
// log debug message
ArkLoggerFactory.getDefaultLogger().debug(
“Fail to load export class “ + name, e);
}
}
}
}
1先部署 DeployPluginStage
com.alipay.sofa.ark.container.model.PluginModel#start
publicvoid start() throws ArkRuntimeException {
if (activator == null || activator.isEmpty()) {
return;
}
EventAdminService eventAdminService = ArkServiceContainerHolder.getContainer().getService(
EventAdminService.class);
ClassLoader oldClassLoader = ClassLoaderUtils
.pushContextClassLoader(this.pluginClassLoader);
try {
eventAdminService.sendEvent(new BeforePluginStartupEvent(this));
// 1使用自己的PluginClassLoader加载插件的启动activator类
pluginActivator = (PluginActivator) pluginClassLoader.loadClass(activator)
.newInstance();
// 2 启动插件activator
pluginActivator.start(pluginContext);
} catch (Throwable ex) {
thrownew ArkRuntimeException(ex.getMessage(), ex);
} finally {
eventAdminService.sendEvent(new AfterPluginStartupEvent(this));
ClassLoaderUtils.popContextClassLoader(oldClassLoader);
}
}
public final static String ACTIVATOR_ATTRIBUTE = “activator”;
com.alipay.sofa.ark.spi.service.PluginActivator WebPluginActivatorZookeeperConfigActivator
publicvoid deploy(String[] args) throws ArkRuntimeException {
ServiceReference
.referenceService(BizDeployer.class);
bizDeployer = serviceReference.getService();
LOGGER.info(String.format(“BizDeployer=\’%s\’ is starting.”, bizDeployer.getDesc()));
bizDeployer.init(args);
bizDeployer.deploy();
}
进入BizModel.start方法,依然使用了MainMethodRunner这个工具类来启动当前biz模块,参数mainClass是biz模块中有main方法的启动类,是在创建BizModel对象的时候设置的每个biz模块也会由一个独立的BizClassLoader负责加载模块所有的类