本文基于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());
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。