Druid中的Extension在啓動時是如何加載的?

Druid的Extension在開發時,按照Druid的Extension編寫規則,啓動Module寫在文件:

META-INF/services/io.druid.initialization.DruidModule

中。如HDFS存儲擴展,其文件內容就爲:io.druid.storage.hdfs.HdfsStorageDruidModule

Druid在啓動時,首先加載配置文件:

在Main.java的main()啓動方法中,初始化startUpInjector:

final Injector injector = GuiceInjectors.makeStartupInjector();

在這個方法中初始化的是:

  public static Collection<Module> makeDefaultStartupModules()
  {
    return ImmutableList.<Module>of(
        new DruidGuiceExtensions(),
        new JacksonModule(),
        // 此處加載兩個配置文件,一個在部署的conf/druid/_common/common.runtime.properties,加載通用的配置
        // 一個在啓動部件的目錄下。比如如果啓動broker,就在conf/druid/broker/runtime.properties,加載broker的配置
        // 這兩個文件裏面所有的鍵值對都放在Properties中。
        new PropertiesModule(Arrays.asList("common.runtime.properties", "runtime.properties")),
        new ConfigModule(),
        new Module()
        {
          @Override
          public void configure(Binder binder)
          {
            binder.bind(DruidSecondaryModule.class);
            // 此處將druid.extensions爲前綴的配置映射到ExtensionConfig類中。
            // druid.extensions後面的key即爲ExtensionConfig的字段名。其值就是這個字段對應的值。
            JsonConfigProvider.bind(binder, "druid.extensions", ExtensionsConfig.class);
          }
        }
    );
  }

以下方法將上面初始化的對象全部注入到Guice中:

  public static Injector makeStartupInjector()
  {
    return Guice.createInjector(makeDefaultStartupModules());
  }


這樣。ExtensionConfig就初始化完了。

下面以啓動Broker爲例,看下Broker下面的Extensions是怎麼加載起來的:

啓動命令:

java `cat conf-quickstart/druid/broker/jvm.config | xargs` -cp "conf-quickstart/druid/_common:conf-quickstart/druid/broker:lib/*" io.druid.cli.Main server broker

會在Druid中啓動CliBroker.java的run()方法【方法在其基類ServerRunnable中】:

  @Override
  public void run()
  {
    final Injector injector = makeInjector();
    final Lifecycle lifecycle = initLifecycle(injector);

    try {
      lifecycle.join();
    }
    catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }

其中,makeInjector()方法如下:

  public Injector makeInjector()
  {
    try {
      return Initialization.makeInjectorWithModules(
          baseInjector, getModules()
      );
    }
    catch (Exception e) {
      throw Throwables.propagate(e);
    }
  }

其中:

  public static Injector makeInjectorWithModules(final Injector baseInjector, Iterable<? extends Module> modules)
  {
    final ModuleList defaultModules = new ModuleList(baseInjector);
    defaultModules.addModules(
        // New modules should be added after Log4jShutterDownerModule
        new Log4jShutterDownerModule(),
        new DruidAuthModule(),
        new LifecycleModule(),
        EmitterModule.class,
        HttpClientModule.global(),
        new HttpClientModule("druid.broker.http", Client.class),
        new CuratorModule(),
        new AnnouncerModule(),
        new DruidProcessingModule(),
        new AWSModule(),
        new MetricsModule(),
        new ServerModule(),
        new StorageNodeModule(),
        new JettyServerModule(),
        new QueryableModule(),
        new QueryRunnerFactoryModule(),
        new DiscoveryModule(),
        new ServerViewModule(),
        new MetadataConfigModule(),
        new DerbyMetadataStorageDruidModule(),
        new JacksonConfigManagerModule(),
        new IndexingServiceDiscoveryModule(),
        new CoordinatorDiscoveryModule(),
        new LocalDataStorageDruidModule(),
        new FirehoseModule(),
        new ParsersModule(),
        new JavaScriptModule(),
        new StartupLoggingModule()
    );

    ModuleList actualModules = new ModuleList(baseInjector);
    actualModules.addModule(DruidSecondaryModule.class);
    for (Object module : modules) {
      actualModules.addModule(module);
    }

    Module intermediateModules = Modules.override(defaultModules.getModules()).with(actualModules.getModules());

    ModuleList extensionModules = new ModuleList(baseInjector);
    final ExtensionsConfig config = baseInjector.getInstance(ExtensionsConfig.class);
    for (DruidModule module : Initialization.getFromExtensions(config, DruidModule.class)) {
      extensionModules.addModule(module);
    }

    return Guice.createInjector(Modules.override(intermediateModules).with(extensionModules.getModules()));
  }

在這個方法裏,我們看到了Extensions中DruidModule的加載方法:Initialization.getFromExtensions(config, DruidModule.class)

進這個方法中:

  public synchronized static <T> Collection<T> getFromExtensions(ExtensionsConfig config, Class<T> clazz)
  {
    final Set<T> retVal = Sets.newHashSet();
    final Set<String> loadedExtensionNames = Sets.newHashSet();

    if (config.searchCurrentClassloader()) {
      for (T module : ServiceLoader.load(clazz, Thread.currentThread().getContextClassLoader())) {
        final String moduleName = module.getClass().getCanonicalName();
        if (moduleName == null) {
          log.warn(
              "Extension module [%s] was ignored because it doesn't have a canonical name, is it a local or anonymous class?",
              module.getClass().getName()
          );
        } else if (!loadedExtensionNames.contains(moduleName)) {
          log.info("Adding classpath extension module [%s] for class [%s]", moduleName, clazz.getName());
          loadedExtensionNames.add(moduleName);
          retVal.add(module);
        }
      }
    }

    for (File extension : getExtensionFilesToLoad(config)) {
      log.info("Loading extension [%s] for class [%s]", extension.getName(), clazz.getName());
      try {
        final URLClassLoader loader = getClassLoaderForExtension(extension);
        for (T module : ServiceLoader.load(clazz, loader)) {
          final String moduleName = module.getClass().getCanonicalName();
          if (moduleName == null) {
            log.warn(
                "Extension module [%s] was ignored because it doesn't have a canonical name, is it a local or anonymous class?",
                module.getClass().getName()
            );
          } else if (!loadedExtensionNames.contains(moduleName)) {
            log.info("Adding local file system extension module [%s] for class [%s]", moduleName, clazz.getName());
            loadedExtensionNames.add(moduleName);
            retVal.add(module);
          }
        }
      }
      catch (Exception e) {
        throw Throwables.propagate(e);
      }
    }

    // update the map with currently loaded modules
    extensionsMap.put(clazz, retVal);

    return retVal;
  }


我們看到,在這裏,Druid使用java自帶的ServiceLoader,將ExtensionConfig中loadList所配置的Extension全部load起來。

其中:

  public static File[] getExtensionFilesToLoad(ExtensionsConfig config)
  {
    final File rootExtensionsDir = new File(config.getDirectory());
    if (rootExtensionsDir.exists() && !rootExtensionsDir.isDirectory()) {
      throw new ISE("Root extensions directory [%s] is not a directory!?", rootExtensionsDir);
    }
    File[] extensionsToLoad;
    final List<String> toLoad = config.getLoadList();
    if (toLoad == null) {
      extensionsToLoad = rootExtensionsDir.listFiles();
    } else {
      int i = 0;
      extensionsToLoad = new File[toLoad.size()];
      for (final String extensionName : toLoad) {
        final File extensionDir = new File(rootExtensionsDir, extensionName);
        if (!extensionDir.isDirectory()) {
          throw new ISE(
              String.format(
                  "Extension [%s] specified in \"druid.extensions.loadList\" didn't exist!?",
                  extensionDir.getAbsolutePath()
              )
          );
        }
        extensionsToLoad[i++] = extensionDir;
      }
    }
    return extensionsToLoad == null ? new File[]{} : extensionsToLoad;
  }


其中ExtensionConfig中loadList配置的,是Druid部署根目錄的extensions目下的擴展目錄名字。通過以上方法,可以找到每個配置的Extension的

META-INF/services/io.druid.initialization.DruidModule

文件。

然後通過相關ClassLoader將其啓動Module Load起來:

  public static URLClassLoader getClassLoaderForExtension(File extension) throws MalformedURLException
  {
    URLClassLoader loader = loadersMap.get(extension.getName());
    if (loader == null) {
      final Collection<File> jars = FileUtils.listFiles(extension, new String[]{"jar"}, false);
      final URL[] urls = new URL[jars.size()];
      int i = 0;
      for (File jar : jars) {
        final URL url = jar.toURI().toURL();
        log.info("added URL[%s]", url);
        urls[i++] = url;
      }
      loader = new URLClassLoader(urls, Initialization.class.getClassLoader());
      loadersMap.put(extension.getName(), loader);
    }
    return loader;
  }


發佈了45 篇原創文章 · 獲贊 19 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章