(三)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; }
相关推荐
网页模板——基于jQuery Bootstrap3的文本编辑器特效源码
bootstrap源码.rar
前端框架利器——bootstrap
HTML5期末考核大作业源码 包含 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞 蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、游戏、 节日、 戒烟、 电影、 ...
中介效应分析原理、程序、Bootstrap方法及其应用,中介效应分析原理、程序、Bootstrap方法及其应用
springboot+bootstrap博客系统源码
Bootstrap源码,用于移动前端开发,解压后可以直接导入到项目中
网页模板——基于Bootstrap仿Pinterest实现超酷的网格瀑布流插件特效源码
bootstrap源代码,前端框架,前端开发
软UI设计系统——开源Bootstrap5设计系统.zip
网页模板——基于HTML5 Bootstrap 3.x可预览的文件上传特效源码
bootstrap源码实例
html5+bootstrap网页源码,分享大家学习,Bootstrap是Twitter推出的一个用于前端开发的开源工具包。它由Twitter的设计师Mark Otto和Jacob Thornton合作开发,是一个CSS/HTML框架。
这是一个node.js的博客案例,代码编写规范,适合初学者阅读.
Front-end development with ASP.NET Core,Angular and Bootstrap web前端开发 使用ASP.NET Core,Angular 和Bootstrap 全书源码
基于Bootstrap的管理后台模板源码
Asp.net MVC快速开发框架源码 mvc+Bootstrap 框架使用场景:OA、ERP、BPM、CRM、WMS、TMS、MIS等业务管理系统及后台系统
bootstrap源代码,也可以直接到官方网站下载;
springmvc mybatis bootstrap 源码小项目,共同学习 免费下载 官方不要自动调了
Bootstrap是美国Twitter公司的设计师Mark Otto和Jacob Thornton合作基于HTML、CSS、JavaScript 开发的简洁... [2] 国内一些移动开发者较为熟悉的框架,如WeX5前端开源框架等,也是基于Bootstrap源码进行性能优化而来。