习惯使用spark-submit提交python写的pyspark脚本,突然想开发基于springboot开发java spark代码。在实际开发工程中,由于对springboot不熟,遇到了很多问题,好在最终都解决了。以下记录了一些问题及其解决方法。
本文以统计日志中的累积用户和月活用户为例,进行说明:
**测试数据**
格式:parquet, schema信息如下:
root
|-- userid: string (nullable = true)
|-- ts: integer (nullable = true)
数据样式:
+----------------------------------------+----------+
|userid |ts |
+----------------------------------------+----------+
|6E26E7B94E52DC4AD9712320035F12784F54677A|1595116043|
|498AD603A26FCA20789DE46A69067AAB4F445535|1595139810|
|E64DA425FED067B0FA2717329E72169B4D545577|1595152077|
|D05ADF5CD7FD4134A6DF601A6A17D9C64E7A6B7A|1595119558|
|443DB7862123909484EAF84B0699E93F4F444D7A|1595143797|
+----------------------------------------+----------+
**实现目标**
统计日志中的累积用户和月活用户
**处理过程**:
将今日份的日活数据合入累积数据中,并对累积数据去重。若用户相同,保留时间最新的。
完整工程代码见文章1
代码结构如下图:
3.1 common包存在常量、分隔符;</br>
3.2 config包存在spark配置;</br>
3.3 entity包存在命令行参数,主要通过JobParamEntity进行参数共享;</br>
3.4 task目录实现所有的业务逻辑,其中DoPrepareTask组装输入输出目录,DoInitTask初始化SparkSession和UDF,DoProcessTask实现业务逻辑;</br>
3.5 udf包实现所有UDF;</br>
3.6 util包存放常用工具类。
├─data
│ ├─input
│ │ └─20200719
│ └─output
│ ├─month
│ │ └─20200719
│ └─total
│ ├─20200718
│ └─20200719
├─script
├─src
├─main
│ ├─java
│ │ └─com
│ │ └─demo
│ │ └─sparksubmit
│ │ ├─common
│ │ ├─config
│ │ ├─entity
│ │ ├─task
│ │ ├─udf
│ │ └─util
│ └─resources
└─test
└─java
└─com
└─demo
└─sparksubmit
**报错1: java.lang.ClassNotFoundException:org.apache.spark.sql.SparkSession** </br>
解决:本地调试时,需要引入spark相关的依赖,且不能为provided
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core\_${scale.version}</artifactId>
<version>${spark.version}</version>
<!--调试时注解,打包时添加-->
<!--<scope>provided</scope>-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql\_${scale.version}</artifactId>
<version>${spark.version}</version>
<!--调试时注解,打包时添加-->
<!--<scope>provided</scope>-->
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive\_${scale.version}</artifactId>
<version>${spark.version}</version>
<!--调试时注解,打包时添加-->
<!--<scope>provided</scope>-->
</dependency>
**报错1:A master URL must be set in your configuration**</br>
在本地测试时,设置master为local模式,但是在打成jar时,需要将其注解
SparkSession spark = SparkSession.builder()
//.master("local[\*]")
.appName("spark-submit-demo")
.enableHiveSupport()
.getOrCreate();
但是在打包时一直出现上述报错信息,文章2说是SparkSession在driver的main函数外初始化导致代码无法分发。经尝试调试SparkSession代码也没能解决这个问题。后来从打包的日志中,发现运行了spark代码。经排查发现是执行springbootTest时因未master而报错。注解掉即可解决问题:
// SpringBootTest会执行代码,若未设置master会报错
//@SpringBootTest
class GetidApplicationTests {
@Test
void contextLoads() {
}
}
**报错2:com.google.gson.GsonBuilder.setLenient 或者 compatible version of com.google.gson.GsonBuilder** </br>
文章3指出为gson的版本太低,引入新版本即可解决这个问题,遗憾的是未能解决问题。
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<!--此时的最新版本-->
<version>2.8.6</version>
</dependency>
文章4中指出是在运行时,Spark的gson包覆盖了新版本,需要在配置启动参数userClassPathFirst,遗憾的是未能解决问题。
${SPARK\_SUBMIT} \
--conf spark.executor.userClassPathFirst=true\
--conf spark.driver.userClassPathFirst=true \
...
文章5指出由于springboot自动加载配置导致加载spark的gson出错,可以通过exclude加载解决。问题终于得以解决。
//(exclude = {GsonAutoConfiguration.class})
@SpringBootApplication(exclude = {GsonAutoConfiguration.class})
**报错3: No auto configuration classes found in META-INF/spring.factories 或者 java.lang.ClassCastException: cannot assign instance of scala.collection.immutable.List**</br>
这个主要是打包方式不同,导致错误不同,仅第3种方式可正常运行
<!--可打包,可本地spark-submit, 但是不能在集群中运行-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<mainClass>${start-class}</mainClass>
<excludes>
<exclude>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core\_${scale.version}</artifactId>
</exclude>
<exclude>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql\_${scale.version}</artifactId>
</exclude>
<exclude>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive\_${scale.version}</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!--可打包,本地运行报错:找不到主类-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.4</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>${start-class}</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
本章6指出springboot 打spark-submit包的正确方式
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope>
<createDependencyReducedPom>false</createDependencyReducedPom>
<filters>
<filter>
<artifact>\*:\*</artifact>
<excludes>
<exclude>META-INF/\*.SF</exclude>
<exclude>META-INF/\*.DSA</exclude>
<exclude>META-INF/\*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transforme
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transforme
implementation="org.springframework.boot.maven.PropertiesMergingResourceTransformer">
<resource>META-INF/spring.factories</resource>
</transformer>
<transforme
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
<transforme
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transforme
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${start-class}</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
由于对springboot的不了解,只能去一一解决表象问题,抓不到本质,后续还需更加努力。
****
1 工程代码https://github.com/Lmikic101/sparksubmit_demo</br>
4 spark程序jar与spark lib jar冲突,加载顺序,https://www.jianshu.com/p/0fe48bc43a8c</br>
6 spark submit spring boot application, https://blog.csdn.net/hzs33/article/details/83183217
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。