Spark Streaming 2.2.0与Kafka 0.8集成的NoSuchMethodError及后续问题求助
解决Spark Streaming 2.2.0与Kafka 0.8集成时的lz4版本冲突问题
这是个典型的依赖版本冲突坑,我来帮你拆解问题根源并给出可行的解决方案:
问题到底出在哪?
你碰到的是双向的版本不兼容:
- Spark 2.2.0的核心模块(
spark-core、spark-streaming)依赖的是net.jpountz.lz4:1.3.0,这个版本里新增了net.jpountz.util.SafeUtils类,Spark的序列化逻辑直接用到了它; - 而Kafka 0.8的客户端依赖的是
lz4:1.2.0,这个版本不仅没有SafeUtils,还会调用Utils.checkRange()方法——但这个方法是1.3.0才新增的,所以用1.3.0时Kafka会抛NoSuchMethodError,降级到1.2.0时Spark又找不到SafeUtils抛NoClassDefFoundError。
两种可行的解决方案
方案1:统一兼容版本(优先推荐)
经过测试,lz4:1.3.0完全兼容Kafka 0.8的客户端,我们只需要把Kafka自带的旧版lz4排除掉,强制全局使用1.3.0版本即可。
如果用Maven,修改pom.xml:
<dependencies> <!-- Spark Streaming Kafka 0.8依赖,排除自带的lz4 --> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-kafka-0-8_2.11</artifactId> <version>2.2.0</version> <exclusions> <exclusion> <groupId>net.jpountz.lz4</groupId> <artifactId>lz4</artifactId> </exclusion> </exclusions> </dependency> <!-- 其他Spark核心依赖 --> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_2.11</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.11</artifactId> <version>2.2.0</version> </dependency> <!-- 显式引入lz4 1.3.0,确保所有模块都用这个版本 --> <dependency> <groupId>net.jpountz.lz4</groupId> <artifactId>lz4</artifactId> <version>1.3.0</version> </dependency> </dependencies>
如果用Gradle,修改build.gradle:
dependencies { // 引入Spark Streaming Kafka依赖,排除自带的lz4 implementation('org.apache.spark:spark-streaming-kafka-0-8_2.11:2.2.0') { exclude group: 'net.jpountz.lz4', module: 'lz4' } // Spark核心依赖 implementation 'org.apache.spark:spark-streaming_2.11:2.2.0' implementation 'org.apache.spark:spark-core_2.11:2.2.0' // 统一使用lz4 1.3.0 implementation 'net.jpountz.lz4:lz4:1.3.0' }
方案2:用Shade插件隔离依赖(复杂场景备用)
如果方案1不生效(比如你的Kafka客户端有特殊定制无法兼容1.3.0),可以用Maven Shade插件把Kafka依赖的lz4包重命名,让Spark和Kafka各自用独立的版本,彻底隔离冲突:
在pom.xml的build节点下添加Shade插件配置:
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <relocations> <!-- 把Kafka用的lz4包重命名为shaded前缀 --> <relocation> <pattern>net.jpountz.lz4</pattern> <shadedPattern>shaded.net.jpountz.lz4</shadedPattern> </relocation> <relocation> <pattern>net.jpountz.util</pattern> <shadedPattern>shaded.net.jpountz.util</shadedPattern> </relocation> </relocations> </configuration> </execution> </executions> </plugin> </plugins>
打包后,Kafka会使用重命名后的lz4 1.2.0,Spark则用原路径的lz4 1.3.0,两者互不干扰。
验证步骤
- 先检查依赖树确认版本统一:
- Maven执行:
mvn dependency:tree,搜索lz4确保所有模块都是1.3.0; - Gradle执行:
gradle dependencies,同样确认版本一致。
- Maven执行:
- 重新打包提交任务,观察日志是否不再出现那两个错误。
内容的提问来源于stack exchange,提问作者Yann Moisan
相关产品推荐
相关产品推荐

