首页 > 解决方案 > ClassNotFoundException 即使 jar tvf 显示“缺失”类

问题描述

当我在非本地环境中通过已部署的 jar(由 maven shade 插件构建)运行 spark 应用程序时遇到一个令人困惑的问题。

java.lang.RuntimeException: java.lang.ClassNotFoundException: org.postgresql.ds.PGSimpleDataSource
    at com.zaxxer.hikari.util.UtilityElf.createInstance(UtilityElf.java:96)
    at com.zaxxer.hikari.pool.PoolBase.initializeDataSource(PoolBase.java:314)
    at com.zaxxer.hikari.pool.PoolBase.<init>(PoolBase.java:108)
    at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:105)
    at com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:72)
    at mypackage.SansORMProvider.get(SansORMProvider.java:42)
    at mypackage.MySansORMProvider.get(MySansORMProvider.scala:15)
    at mypackage.MyApp$.main(MyApp.scala:63)
    at mypackage.MyApp.main(MyApp.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.spark.deploy.yarn.ApplicationMaster$$anon$2.run(ApplicationMaster.scala:680)
Caused by: java.lang.ClassNotFoundException: org.postgresql.ds.PGSimpleDataSource
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    at com.zaxxer.hikari.util.UtilityElf.createInstance(UtilityElf.java:83)
    ... 13 more

这令人困惑的原因是因为我的 pom.xml 中有以下内容:

<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <scope>compile</scope>
</dependency>

shade 插件没有配置引用这个 postgres 依赖或任何匹配它的模式。

<plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <phase>package</phase>
                            <configuration>
                                <artifactSet>
                                    <excludes combine.children="append">
                                        <exclude>org.apache.spark:*:*</exclude>
                                        <exclude>org.apache.hadoop:*:*</exclude>
                                        <exclude>org.slf4j:*</exclude>
                                    </excludes>
                                </artifactSet>
                                <filters>
                                    <filter>
                                        <artifact>*:*</artifact>
                                        <excludes>
                                            <exclude>META-INF/*.SF</exclude>
                                            <exclude>META-INF/*.DSA</exclude>
                                            <exclude>META-INF/*.RSA</exclude>
                                        </excludes>
                                    </filter>
                                </filters>
                                <relocations>
                                    <relocation>
                                        <pattern>com.google.common</pattern>
                                        <shadedPattern>${project.groupId}.google.common</shadedPattern>
                                    </relocation>
                                    <relocation>
                                        <pattern>io.netty</pattern>
                                        <shadedPattern>${project.groupId}.io.netty</shadedPattern>
                                    </relocation>
                                    <relocation>
                                        <pattern>okhttp3</pattern>
                                        <shadedPattern>${project.groupId}.okhttp3</shadedPattern>
                                    </relocation>
                                    <relocation>
                                        <pattern>com.fasterxml.jackson</pattern>
                                        <shadedPattern>${project.groupId}.fasterxml.jackson</shadedPattern>
                                    </relocation>
                                    ]
                                </relocations>
                                <shadedArtifactAttached>true</shadedArtifactAttached>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>

Spark 依赖项(根据要求):

<dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-core_2.11</artifactId>
                <version>2.4.3</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-mllib_2.11</artifactId>
                <version>2.4.3</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-sql_2.11</artifactId>
                <version>2.4.3</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-tags_2.11</artifactId>
                <version>2.4.3</version>
                <scope>provided</scope>
            </dependency>

在构建 jar 的 maven 命令的输出中,我可以看到[INFO] Including org.postgresql:postgresql:jar:42.2.1 in the shaded jar.

当我跑步时,jar tvf myShadedJar.jar | grep postgres我可以看到丢失的课程。

可能相关的一件奇怪的事情是,当我实际解压缩jar xf没有 org/postgresql 文件夹的 jar 时。然而,当我unzip进入罐子时,它就在那里。

可能是什么问题?我该如何确认?预计爆炸的 jar 是否缺少 org/postgresql 文件夹?

标签: javamavenapache-sparkmaven-shade-plugin

解决方案


在将我们的 Spark 应用程序从 AWS EMR 5.24.1 迁移到 5.33.0 时,我遇到了完全相同的问题。经过几周的定期尝试寻找出路后,我最终意识到我们的 uber-jar 中的 HikariCP 包在执行期间没有被使用。当我从胖罐中排除 HikariCP 并且错误没有改变时,这一点变得很明显,尽管我预计它会抱怨找不到 HikariCP。
事实证明,EMR 5.33.0 在多个 lib 文件夹中有HikariCP-java7-2.4.12.jar,并且在运行时使用了这个包(而不是 uber jar 中的那个)。我只是从主节点和核心节点中删除了这个包的所有场合,这就解决了这个问题。

我希望这对那些对类似问题感到沮丧的人有所帮助。


推荐阅读