构建包含Java Spark单元测试的fat jar后提交Spark任务运行失败
问题根因
你遇到的异常本质不是Spark会话资源未关闭,原因如下:
- 你单元测试中使用了
HiveContext,默认会在执行测试的工作目录下生成基于Derby的元数据存储目录metastore_db以及日志文件derby.log - 你执行构建打fat jar时,构建工具(Maven/Gradle)默认会把工作目录下的所有非排除文件打入jar包,而jar包内的所有文件都是只读状态
- 提交Spark任务运行时,
HiveContext会尝试读写jar包内的metastore_db,触发Derby数据库的只读权限异常 - 单独在IDEA运行单元测试不会触发jar包构建,因此
metastore_db不会被打入jar,自然不会出现运行异常
解决方案
方案1:修改构建配置排除元数据文件
在打fat jar的插件配置中添加排除规则,禁止将metastore_db、derby.log打入jar包:
Maven(以maven-assembly-plugin为例):
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <excludes> <exclude>metastore_db/**</exclude> <exclude>derby.log</exclude> </excludes> <!-- 其余原有配置 --> </configuration> </plugin>
Gradle:
jar { exclude('metastore_db/**') exclude('derby.log') // 其余原有配置 }
方案2:单元测试指定元数据存储到临时目录
在单元测试的SparkConf中指定metastore存储路径到构建临时目录(比如Maven的target目录,Gradle的build目录,默认不会被打入jar也会每次构建自动清理):
修改你setUp方法中的SparkConf配置:
SparkConf conf = new SparkConf(); conf.setMaster("local[2]"); conf.setAppName("sparkHelperTest"); // 新增下面这行配置,指定元数据存储到target/metastore_db conf.set("javax.jdo.option.ConnectionURL", "jdbc:derby:;databaseName=target/metastore_db;create=true"); sparkContext = new JavaSparkContext(conf);
方案3:单元测试避免使用HiveContext
如果你的单元测试不需要用到Hive相关特性,直接替换HiveContext为普通SQLContext,不会生成metastore_db目录,从根源避免问题。
临时规避方案
打fat jar时添加跳过测试的参数,避免构建阶段执行单元测试生成metastore_db:
- Maven命令添加
-DskipTests - Gradle命令添加
-x test
内容的提问来源于stack exchange,提问作者Laodao
相关产品推荐
相关产品推荐

