解析S4中node的启动原理(三)——S4BootStrap源码分析

(三)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;
        }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值