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;
  }


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值