前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Apache NIFI项目结构的类资源隔离机制

Apache NIFI项目结构的类资源隔离机制

作者头像
@阿诚
发布2020-09-01 15:43:03
发布2020-09-01 15:43:03
1.7K00
代码可运行
举报
文章被收录于专栏:Panda诚Panda诚
运行总次数:0
代码可运行

前言

本文简单的讨论一下Apache NIFI项目结构的类资源隔离机制,适合接触过源码的同学阅读。

NIFI的常见的子Moudle结构

以nifi-flume-bundle为例

代码语言:javascript
代码运行次数:0
运行
复制
nifi-flume-bundle
    -- nifi-flume-processors
    -- nifi-flume-nar

nifi-flume-bundle 有两个子项目,nifi-flume-processors里是Processor的具体实现,打成jar包。nifi-flume-nar里没有代码实现负责将nifi-flume-processors.jar及其依赖打成nar包。

NAR是什么?

NAR是NiFi Archive的缩写,创建nar的原因是为了实现Java类加载器隔离资源。NIFI的组件实现都来自不同的公司和贡献者,代码里往往会引入不同版本的第三方库(比如apache-commons等)。NAR文件避免了NoClassDefFoundError异常的出现(这些异常是由于在不同处理器的类加载器中已经加载了错误版本的依赖而引发的)。

NAR文件结构

代码语言:javascript
代码运行次数:0
运行
复制
META-INF
    bundled-dependencies    
        async-1.4.0.jar
        ...
        flume-dataset-sink-1.6.0.jar
        ...
        nifi-flume-processors-1.11.4.jar
        nifi-utils-1.11.4.jar
        ...
    DEPENDENCIES
    docs
        ...
    LICENSE
    MANIFEST.MF
    maven
        org.apache.nifi
            nifi-flume-nar
                pom.properties
                pom.xml
    NOTICE

NAR文件实际上跟WAR和NAR差不多,但有一些区别。NAR的根目录是META-INF(与JAR中一样,而WAR是WEB-INF)。META-INF根目录下是描述性文件,例如LICENSE,DEPENDENCIES(列出捆绑的依赖项的许可证信息)和NOTICE(包含处理器本身的许可证)。

此外,META-INF下还有3个关键文件和目录。首先是MANIFEST.MF文件,它跟jar里的文件基本相同,但是,其中多包含一些NAR信息,比如Nar-Id用来识别nar,Nar-Version是NAR里处理器的版本,Nar-Dependency-Id是当前NAR所依赖的NAR的ID(nar不能依赖多个其他nar)等等,还包括有关用于构建NAR的Java和Maven版本以及其来源的有用元数据。

代码语言:javascript
代码运行次数:0
运行
复制
Manifest-Version: 1.0
Build-Branch: develop
Build-Timestamp: 2020-07-09T11:08:58Z
Archiver-Version: Plexus Archiver
Nar-Dependency-Group: org.apache.nifi
Built-By: zhangcheng
Nar-Id: nifi-flume-nar
Clone-During-Instance-Class-Loading: false
Nar-Dependency-Version: 1.11.4
Nar-Version: 1.11.4
Build-Tag: nifi-1.11.4-RC1
Build-Revision: 7c28976
Nar-Group: org.apache.nifi
Nar-Dependency-Id: nifi-hadoop-libraries-nar
Created-By: Apache Maven 3.6.1
Build-Jdk: 1.8.0_181

maven目录包含用于构建NAR的POM文件(Maven构建描述文件),以及一个pom.properties文件,其中包含NAR的maven兼容详细信息(maven用于依赖性识别的3个关键元素,groupId,artifiactId,version)

代码语言:javascript
代码运行次数:0
运行
复制
#Generated by Maven
#Thu Jul 09 11:08:58 CST 2020
version=1.11.4
groupId=org.apache.nifi
artifactId=nifi-flume-nar

bundled-dependencies目录下是组件的jar包及其依赖的其他jar包,这些jar会被特定的类加载器加载。

以nar为基础的类资源隔离

在NIFI启动时,会把lib目录下的nar文件都解压到work/nar目录下。其中nifi-framework-nar-1.11.4.nar会单独解压到work/nar/framework/下,其他的nar文件都解压到work/nar/extensions/下,对应的目录类似于nifi-flume-nar-1.11.4.ar-unpacked

在NIFI启动源码解读的NiFi.java 源码解读和NIFI Nar包加载机制源码解读中我们说过每一个nar包对应创建一个类加载器,使用不同的类加载器去加载这个nar资源。

NarClassLoader

遵循JAVA的类加载器委托加载机制,由NarClassLoaders的createNarClassLoader创建

代码语言:javascript
代码运行次数:0
运行
复制
private static ClassLoader createNarClassLoader(final File narDirectory, final ClassLoader parentClassLoader) throws IOException, ClassNotFoundException {
    logger.debug("Loading NAR file: " + narDirectory.getAbsolutePath());
    final ClassLoader narClassLoader = new NarClassLoader(narDirectory, parentClassLoader);
    logger.info("Loaded NAR file: " + narDirectory.getAbsolutePath() + " as class loader " + narClassLoader);
    return narClassLoader;
}

每一个nar包对应创建一个类加载器,源码里JettyServer.start()里启动jetty前调用extensionManager.discoverExtensions(systemBundle, bundles);(bundles里记录了nar信息和对应的类加载器),

代码语言:javascript
代码运行次数:0
运行
复制
@Override
public void discoverExtensions(final Set<Bundle> narBundles) {
    // get the current context class loader
    ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader();

    // consider each nar class loader
    for (final Bundle bundle : narBundles) {
        // Must set the context class loader to the nar classloader itself
        // so that static initialization techniques that depend on the context class loader will work properly
        final ClassLoader ncl = bundle.getClassLoader();
        Thread.currentThread().setContextClassLoader(ncl);
        loadExtensions(bundle);

        // Create a look-up from coordinate to bundle
        bundleCoordinateBundleLookup.put(bundle.getBundleDetails().getCoordinate(), bundle);
    }

    // restore the current context class loader if appropriate
    if (currentContextClassLoader != null) {
        Thread.currentThread().setContextClassLoader(currentContextClassLoader);
    }
}

在loadExtensions(bundle)里使用SPI机制ServiceLoader去加载各个组件的class信息,而组件代码所涉及的其他类的class也会隐式的由当前组件的Class对象中引用的类加载器去加载,这样就完成了整个项目架构以nar为基础的类资源隔离。

代码语言:javascript
代码运行次数:0
运行
复制
private void loadExtensions(final Bundle bundle) {
        for (final Map.Entry<Class, Set<Class>> entry : definitionMap.entrySet()) {
            final boolean isControllerService = ControllerService.class.equals(entry.getKey());
            final boolean isProcessor = Processor.class.equals(entry.getKey());
            final boolean isReportingTask = ReportingTask.class.equals(entry.getKey());
            //SPI机制
            final ServiceLoader<?> serviceLoader = ServiceLoader.load(entry.getKey(), bundle.getClassLoader());
            for (final Object o : serviceLoader) {
                try {
                    loadExtension(o, entry.getKey(), bundle);
                } catch (Exception e) {
                    logger.warn("Failed to register extension {} due to: {}" , new Object[]{o.getClass().getCanonicalName(), e.getMessage()});
                    if (logger.isDebugEnabled()) {
                        logger.debug("", e);
                    }
                }
            }

            classLoaderBundleLookup.put(bundle.getClassLoader(), bundle);
        }
    }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-07-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Panda诚 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • NIFI的常见的子Moudle结构
  • NAR是什么?
  • NAR文件结构
  • 以nar为基础的类资源隔离
  • NarClassLoader
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档