本文基于trino的435版本。
trino依赖了airlift,相关使用可以查看airlift的github。
启动方式参考github上的README,通过DevelopmentServer进行启动。

初始化

DevelopmentServer也是调用Server的doStart方法。下面的app.initialize完成相关类的实现和配置信息的注入。

Bootstrap app = new Bootstrap(modules.build());
// 中间略
Injector injector = app.initialize();

由于DevelopmentServer重写了getAdditionalModules方法。这里指定了PluginsProvider的实现类是DevelopmentPluginsProvider,并指定了DevelopmentLoaderConfig配置类。

@Override
protected Iterable<? extends Module> getAdditionalModules()
{
    return ImmutableList.of(binder -> {
        newOptionalBinder(binder, PluginsProvider.class).setBinding()
                .to(DevelopmentPluginsProvider.class).in(Scopes.SINGLETON);
        configBinder(binder).bindConfig(DevelopmentLoaderConfig.class);
    });
}

DevelopmentLoaderConfig配置类读取了plugin.bundles配置,这个就是启动时加载插件列表。

@Config("plugin.bundles")
public DevelopmentLoaderConfig setPlugins(String plugins)
{
    this.plugins = SPLITTER.splitToList(plugins);
    return this;
}

读取配置信息

loadPlugins方法是开始加载plugins的入口,这里的的实现类是PluginManager。

// 中间略
injector.getInstance(PluginInstaller.class).loadPlugins();

PluginManager的loadPlugins方法中,这里的pluginsProvider就是初始化中指定的DevelopmentPluginsProvider。

@Override
public void loadPlugins()
{
    if (!pluginsLoading.compareAndSet(false, true)) {
        return;
    }

    pluginsProvider.loadPlugins(this::loadPlugin, PluginManager::createClassLoader);

    typeRegistry.verifyTypes();
}

DevelopmentPluginsProvider中,实际上是通过多线程的方式读取并处理plugins信息。

@Override
public void loadPlugins(Loader loader, ClassLoaderFactory createClassLoader)
{
    executeUntilFailure(
            executor,
            plugins.stream()
                    .map(plugin -> (Callable<?>) () -> {
                        loader.load(plugin, () -> buildClassLoader(plugin, createClassLoader));
                        return null;
                    })
                    .collect(toImmutableList()));
}

构造PluginClassLoader

这里是读取plugins信息,实际上就是读取pom文件,把pom中依赖的jar包读取出来。

private PluginClassLoader buildClassLoader(String plugin, ClassLoaderFactory classLoaderFactory)
{
    try {
        return doBuildClassLoader(plugin, urls -> classLoaderFactory.create(plugin, urls));
    }
    catch (IOException e) {
        throw new UncheckedIOException(e);
    }
}

private PluginClassLoader doBuildClassLoader(String plugin, Function<List<URL>, PluginClassLoader> classLoaderFactory)
            throws IOException
{
    File file = new File(plugin);
    if (file.isFile() && (file.getName().equals("pom.xml") || file.getName().endsWith(".pom"))) {
        return buildClassLoaderFromPom(file, classLoaderFactory);
    }
    return buildClassLoaderFromCoordinates(plugin, classLoaderFactory);
}

通过PluginManager的createClassLoader方法,把plugin的名称,读取到的所有jar包依赖的URL信息,返回PluginClassLoader。也就是说,每个plugin都有自己的PluginClassLoader。

public static PluginClassLoader createClassLoader(String pluginName, List<URL> urls)
{
    ClassLoader parent = PluginManager.class.getClassLoader();
    return new PluginClassLoader(pluginName, urls, parent, SPI_PACKAGES);
}

如果还有plugin-discovery目录,说明有相应插件,在target/plugin-discovery/META-INF/services/io.trino.spi.Plugin中写入这个插件,同时也要把这个jar包加入到PluginClassLoader。

private PluginClassLoader buildClassLoaderFromPom(File pomFile, Function<List<URL>, PluginClassLoader> classLoaderFactory)
            throws IOException
{
    List<Artifact> artifacts = resolver.resolvePom(pomFile);
    PluginClassLoader classLoader = createClassLoader(artifacts, classLoaderFactory);

    Artifact artifact = artifacts.get(0);
    Set<String> plugins = discoverPlugins(artifact, classLoader);
    if (!plugins.isEmpty()) {
        File root = new File(artifact.getFile().getParentFile().getCanonicalFile(), "plugin-discovery");
        writePluginServices(plugins, root);
        classLoader = classLoader.withUrl(root.toURI().toURL());
    }

    return classLoader;
}

注册PluginClassLoader

构建PluginClassLoader后,有多少个插件就有多个个PluginClassLoader,通过HandleResolver保存插件名称和PluginClassLoader的对应关系。

public void registerClassLoader(PluginClassLoader classLoader)
{
    ClassLoader existingClassLoader = classLoaders.putIfAbsent(classLoader.getId(), classLoader);
    checkState(existingClassLoader == null, "Class loader already registered: %s", classLoader.getId());
}

加载插件

加载插件之前,会设置Classloader为插件对应的PluginClassLoader,这样每个插件所依赖的jar包,都由自己的Classloader来加载,不会造成不同jar包之间的冲突。

try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(pluginClassLoader)) {
    loadPlugin(pluginClassLoader);
}

Classloader的切换是通过ThreadContextClassLoader实现,可以看到每次切换都会保留原理的Classloader,使用结束后再恢复。

public ThreadContextClassLoader(ClassLoader newThreadContextClassLoader)
{
    this.originalThreadContextClassLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(newThreadContextClassLoader);
}

@Override
public void close()
{
    Thread.currentThread().setContextClassLoader(originalThreadContextClassLoader);
}

上面构建PluginClassLoader提到,会写入io.trino.spi.Plugin文件,在加载插件的时候,通过SPI机制,把插件读取出来,

private void loadPlugin(PluginClassLoader pluginClassLoader)
{
    ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, pluginClassLoader);
    List<Plugin> plugins = ImmutableList.copyOf(serviceLoader);
    checkState(!plugins.isEmpty(), "No service providers of type %s in the classpath: %s", Plugin.class.getName(), asList(pluginClassLoader.getURLs()));

    for (Plugin plugin : plugins) {
        log.info("Installing %s", plugin.getClass().getName());
        installPlugin(plugin);
    }
}

installPlugin方法会调用installPluginInternal方法,在installPluginInternal中,这里会进行各种注册,注册的内容取决于实现Plugin的接口,比如MySqlPlugin,继承了JdbcPlugin,JdbcPlugin只实现了getConnectorFactories方法,所以MysqlPlugin加载的时候,就会注册这个方法。

private void installPluginInternal(Plugin plugin){
    // 前面略
    for (ConnectorFactory connectorFactory : plugin.getConnectorFactories()) {
        log.info("Registering connector %s", connectorFactory.getName());
        this.connectorFactory.addConnectorFactory(connectorFactory);
    }
    // 后面略
}

ConnectorFactory注册,实际上就是把插件的名称和ConnectorFactory映射关系保存到map中。

@Override
public synchronized void addConnectorFactory(ConnectorFactory connectorFactory)
{
    ConnectorFactory existingConnectorFactory = connectorFactories.putIfAbsent(
            new ConnectorName(connectorFactory.getName()), connectorFactory);
    checkArgument(existingConnectorFactory == null, "Connector '%s' is already registered", connectorFactory.getName());
}

大军
847 声望183 粉丝

学而不思则罔,思而不学则殆


引用和评论

1 篇内容引用
0 条评论