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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章