(三)S4BootStrap源码分析
S4BootStrap主要做两件事情:(一)向S4中的某个cluster注册该Node;(二)等待新的application发布到该Node所在的cluster上。
S4Bootstrap的构造函数接受三个参数:clusterName,zkClient,fetcher。clusterName的值会被注入,zkClient是一个与zookeeper交互的客户端,fetcher用来根据uri获取文件。先根据clusterName构造一个application的目录(appDir = "/s4/clusters/" + clusterName + "/app";),如果zookeeper上不存在该目录,则在zookeeper上创建一个该目录。然后构造一个appPath,并监听该路径下数据的变化。
public S4Bootstrap(@Named("s4.cluster.name") String clusterName, ZkClient zkClient, ArchiveFetcher fetcher) {
this.fetcher = fetcher;
this.zkClient = zkClient;
String appDir = "/s4/clusters/" + clusterName + "/app";
if (!zkClient.exists(appDir)) {
zkClient.create(appDir, null, CreateMode.PERSISTENT);
}
appPath = appDir + "/s4App";
zkClient.subscribeDataChanges(appPath, new AppChangeListener());
}
S4Bootstrap的start方法,是接受S4Node创建的Injector为参数。因此,start方法可以使用S4Node中多个Modules(主要为BaseModule)中的绑定。判断zookeeper中是否已有appPath,如果存在并且没有部署,则调用loadModulesAndStartApp()方法,加载相关Modules(comm和core)。
public void start(Injector parentInjector) throws InterruptedException, ArchiveFetchException {
this.parentInjector = parentInjector;
if (zkClient.exists(appPath)) {
if (!deployed.get()) {
loadModulesAndStartApp(parentInjector);
}
}
signalOneAppLoaded.await();
}
loadModulesAndStartApp方法根据appPath从zookeeper中读取app的数据,然后获取app的配置信息appConfig。然后获取appConfig中定制的modules的uri。根据每一个uri 和appName,fetchModuleAndCopyToLocalFile方法会将uri对应的Module复制到文件中,所有由Module复制得到的文件被添加到一个List<File>中。该List<File>作为调用ModulesLoaderFactory类的createModulesLoader()的参数,从而将所有的Module加载到classPath下,并且返回一个ModulesLoader的对象。关于ModulesLoaderFactory这个类,我们将在下一部分讨论。
private void loadModulesAndStartApp(final Injector parentInjector) throws ArchiveFetchException {
final ZNRecord appData = zkClient.readData(appPath);
// can be null
final AppConfig appConfig = new AppConfig(appData);
String appName = appConfig.getAppName();
List<File> modulesLocalCopies = new ArrayList<File>();
for (String uriString : appConfig.getCustomModulesURIs()) {
modulesLocalCopies.add(fetchModuleAndCopyToLocalFile(appName, uriString));
}
final ModulesLoader modulesLoader = new ModulesLoaderFactory().createModulesLoader(modulesLocalCopies);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// load app class through modules classloader and start it
startS4App(appConfig, parentInjector, modulesLoader);
signalOneAppLoaded.countDown();
}
}, "S4 platform loader");
t.start();
}
然后创建一个线程,调用startS4App()方法,参数为appConfig,parentInjector,modulesLoader(ModulesLoader的对象)。在startS4App()方法中,首先以appConfig和modulesLoader参数,调用loadApp方法,加载app。然后调用app的init()和start()方法,这样app就被该node成功加载。接下来分析一下用到的几个方法。
private void startS4App(AppConfig appConfig, Injector parentInjector, ClassLoader modulesLoader) {
try {
App app = loadApp(appConfig, modulesLoader);
// use correct classLoader for running the app initialization
Thread.currentThread().setContextClassLoader(app.getClass().getClassLoader());
app.init();
app.start();
} catch (Exception e) {
logger.error("Cannot start S4 node", e);
System.exit(1);
}
}
在loadApp(AppConfig appConfig, ClassLoader modulesLoader)方法中,返回的是一个App类的对象。这个类将在后面讨论。
首先获取其他的module(core和comm)——combinedPlatformModule。如果AppUri==null但是appClassName!=null,用combinedPlatformModule重写了新建的AppModule,然后根据combinedPlatformModule创建了ChildInjector并返回一个app的实例。其中,app.class 是由ModulesLoader加载进来的(Class.forName(appConfig.getAppClassName(), true, modulesLoader))。这个实例强转成App类型返回。
if (appConfig.getAppURI() == null) {
if (appConfig.getAppClassName() != null) {
try {
// In that case we won't be using an S4R classloader, app classes are available from the current
// classloader
// The app module provides bindings specific to the app class loader, in this case the current
// thread's
// class loader.
AppModule appModule = new AppModule(Thread.currentThread().getContextClassLoader());
// NOTE: because the app module can be overriden
Module combinedModule = Modules.override(appModule).with(combinedPlatformModule);
Injector injector = parentInjector.createChildInjector(combinedModule);
logger.info("Starting S4 app with application class [{}]", appConfig.getAppClassName());
return (App) injector.getInstance(Class.forName(appConfig.getAppClassName(), true, modulesLoader));
// server.startApp(app, "appName", clusterName);
} catch (Exception e) {
throw new DeploymentFailedException(String.format(
"Cannot start application: cannot instantiate app class %s due to: %s",
appConfig.getAppClassName(), e.getMessage()), e);
}
} else {
throw new DeploymentFailedException(
"Application class name must be specified when application URI omitted");
}
}
如果AppUri!=null,则在“tmp”目录下创建一个后缀为.s4r的文件——localS4RFileCopy,然后根据AppUri将app拷贝到创建的localS4RFileCopy文件中。同样,根据combinedPlatformModule创建了Injector,然后以Injector,localS4RFileCopy和appName为参数调用loadS4R方法,该方法返回一个App类的对象。
else {
try {
URI uri = new URI(appConfig.getAppURI());
// fetch application
File localS4RFileCopy;
try {
localS4RFileCopy = File.createTempFile("tmp", "s4r");
} catch (IOException e1) {
logger.error(
"Cannot deploy app [{}] because a local copy of the S4R file could not be initialized due to [{}]",
appConfig.getAppName(), e1.getClass().getName() + "->" + e1.getMessage());
throw new DeploymentFailedException("Cannot deploy application [" + appConfig.getAppName() + "]",
e1);
}
localS4RFileCopy.deleteOnExit();
try {
if (ByteStreams.copy(fetcher.fetch(uri), Files.newOutputStreamSupplier(localS4RFileCopy)) == 0) {
throw new DeploymentFailedException("Cannot copy archive from [" + uri.toString() + "] to ["
+ localS4RFileCopy.getAbsolutePath() + "] (nothing was copied)");
}
} catch (Exception e) {
throw new DeploymentFailedException("Cannot deploy application [" + appConfig.getAppName()
+ "] from URI [" + uri.toString() + "] ", e);
}
// install locally
Injector injector = parentInjector.createChildInjector(combinedPlatformModule);
App loadedApp = loadS4R(injector, localS4RFileCopy, appConfig.getAppName());
if (loadedApp != null) {
return loadedApp;
} else {
throw new DeploymentFailedException("Cannot deploy application [" + appConfig.getAppName()
+ "] from URI [" + uri.toString() + "] : cannot start application");
}
} catch (URISyntaxException e) {
throw new DeploymentFailedException(String.format(
"Cannot deploy application [%s] : invalid URI for fetching S4R archive %s : %s", new Object[] {
appConfig.getAppName(), appConfig.getAppURI(), e.getMessage() }), e);
}
}
loadS4R(Injector injector, File s4r, String appName)方法,返回的是一个App的对象。首先根据s4r.getAbsolutePath()创建S4RLoader的一个对象——appClassLoader,然后将s4r打成jar包,并获取该jar包下Manifest文件中MANIFEST_S4_APP_CLASS的属性值,即appName。然后用appClassLoader加载app这个类,并强转成App类型。以appClassLoader为参数创建一个AppModule的对象——appModule,再创建一个Injector将appModule的绑定注入到app中,返回app。
S4RLoaderFactory loaderFactory = injector.getInstance(S4RLoaderFactory.class);
S4RLoader appClassLoader = loaderFactory.createS4RLoader(s4r.getAbsolutePath());
try {
JarFile s4rFile = new JarFile(s4r);
if (s4rFile.getManifest() == null) {
logger.warn("Cannot load s4r archive [{}] : missing manifest file");
return null;
}
if (!s4rFile.getManifest().getMainAttributes().containsKey(new Name(MANIFEST_S4_APP_CLASS))) {
logger.warn("Cannot load s4r archive [{}] : missing attribute [{}] in manifest", s4r.getAbsolutePath(),
MANIFEST_S4_APP_CLASS);
return null;
}
String appClassName = s4rFile.getManifest().getMainAttributes().getValue(MANIFEST_S4_APP_CLASS);
logger.info("App class name is: " + appClassName);
App app = null;
try {
Object o = (appClassLoader.loadClass(appClassName)).newInstance();
app = (App) o;
// we use the app module to provide bindings that depend upon a classloader savy of app classes, e.g.
// for serialization/deserialization
AppModule appModule = new AppModule(appClassLoader);
injector.createChildInjector(appModule).injectMembers(app);
} catch (Exception e) {
logger.error("Could not load s4 application form s4r file [{" + s4r.getAbsolutePath() + "}]", e);
return null;
}
logger.info("Loaded application from file {}", s4r.getAbsolutePath());
signalOneAppLoaded.countDown();
return app;
} catch (IOException e) {
logger.error("Could not load s4 application form s4r file [{" + s4r.getAbsolutePath() + "}]", e);
return null;
}