Spark 4.1.2 + Scala 2.13 开发环境:IntelliJ IDEA 2024.3 与 SBT 1.9.9 配置实战
对于希望构建现代化Spark应用的开发者而言,搭建一个高效的本地开发环境至关重要。本文将详细介绍如何在IntelliJ IDEA 2024.3中配置Spark 4.1.2与Scala 2.13的开发环境,使用SBT 1.9.9作为构建工具,并提供完整的项目配置示例。
1. 环境准备与工具安装
在开始Spark项目开发前,需要确保系统中已安装以下基础组件:
- Java Development Kit (JDK):Spark 4.1.2要求JDK 8或更高版本,推荐使用JDK 11或17以获得最佳兼容性
- Scala 2.13:与Spark 4.1.2兼容的Scala版本
- IntelliJ IDEA 2024.3:安装时需包含Scala插件
- SBT 1.9.9:Scala的构建工具
1.1 JDK安装验证
在终端中运行以下命令检查Java版本:
java -version预期输出应类似于:
openjdk version "17.0.3" 2022-04-19 OpenJDK Runtime Environment (build 17.0.3+7) OpenJDK 64-Bit Server VM (build 17.0.3+7, mixed mode)1.2 Scala环境配置
使用SDKMAN!安装特定版本的Scala:
sdk install scala 2.13.12安装后验证版本:
scala -version正确配置应显示:
Scala code runner version 2.13.12 -- Copyright 2002-2023, LAMP/EPFL and Lightbend, Inc.2. IntelliJ IDEA项目初始化
2.1 创建新SBT项目
- 打开IntelliJ IDEA,选择"New Project"
- 在左侧菜单中选择"Scala",右侧选择"SBT"
- 设置项目名称和位置
- 确保SDK选择正确的JDK版本(如JDK 17)
- SBT版本指定为1.9.9,Scala版本选择2.13.12
- 点击"Create"完成项目创建
2.2 基础项目结构
创建完成后,项目应具有以下目录结构:
my-spark-project/ ├── build.sbt ├── project/ │ ├── build.properties │ └── plugins.sbt ├── src/ │ ├── main/ │ │ ├── resources/ │ │ └── scala/ │ └── test/ │ ├── resources/ │ └── scala/ └── target/3. 配置build.sbt文件
build.sbt是SBT项目的核心配置文件。以下是Spark 4.1.2与Scala 2.13的完整配置示例:
ThisBuild / version := "1.0.0" ThisBuild / scalaVersion := "2.13.12" ThisBuild / organization := "com.example" // 解决Java模块系统在Spark中的警告 ThisBuild / run / fork := true ThisBuild / run / javaOptions += "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED" lazy val root = (project in file(".")) .settings( name := "spark-scala-demo", libraryDependencies ++= Seq( // Spark核心依赖 "org.apache.spark" %% "spark-core" % "4.1.2", "org.apache.spark" %% "spark-sql" % "4.1.2", // 测试依赖 "org.scalatest" %% "scalatest" % "3.2.16" % Test, // 日志处理 "org.apache.logging.log4j" % "log4j-api" % "2.20.0", "org.apache.logging.log4j" % "log4j-core" % "2.20.0", "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.20.0" ), // 并行执行设置 parallelExecution in Test := false, // 编译器选项 scalacOptions ++= Seq( "-deprecation", "-feature", "-unchecked", "-Xlint", "-Ywarn-dead-code", "-Ywarn-numeric-widen", "-Ywarn-unused", "-Ywarn-value-discard" ), // JVM运行参数 javaOptions ++= Seq( "-Xms512M", "-Xmx2G", "-XX:+UseG1GC", "-Dlog4j.configurationFile=log4j2.properties" ) )3.1 关键配置说明
Spark依赖管理:
spark-core和spark-sql是Spark的基础模块- 使用
%%确保获取与Scala 2.13兼容的版本
日志配置:
- 添加Log4j2依赖以避免Spark的日志冲突
- 需要在
src/main/resources下添加log4j2.properties文件
JVM参数:
- 设置合理的堆内存大小(-Xms和-Xmx)
- 使用G1垃圾收集器提高性能
4. 解决常见依赖冲突
Spark项目经常会遇到依赖冲突问题,以下是两个典型场景的解决方案:
4.1 Guava版本冲突
Spark 4.1.2依赖Guava 14.0.1,而许多现代库需要更高版本。解决方案:
dependencyOverrides += "com.google.guava" % "guava" % "32.1.3-jre"4.2 Jackson版本冲突
Spark自带Jackson模块,可能与用户引入的版本冲突:
dependencyOverrides ++= Seq( "com.fasterxml.jackson.core" % "jackson-core" % "2.15.3", "com.fasterxml.jackson.core" % "jackson-databind" % "2.15.3", "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.15.3" )提示:使用
sbt dependencyTree命令可以查看完整的依赖关系树,帮助识别冲突
5. 编写第一个Spark应用
在src/main/scala目录下创建SimpleApp.scala文件:
import org.apache.spark.sql.SparkSession object SimpleApp { def main(args: Array[String]): Unit = { // 创建SparkSession val spark = SparkSession.builder() .appName("Simple Application") .master("local[*]") // 使用本地模式,所有可用核心 .config("spark.sql.shuffle.partitions", "4") // 优化小数据集性能 .getOrCreate() // 减少日志输出 spark.sparkContext.setLogLevel("WARN") import spark.implicits._ try { // 创建示例DataFrame val data = Seq( ("Java", 20000), ("Python", 100000), ("Scala", 3000) ) val df = data.toDF("language", "users") // 执行简单转换 val filteredDF = df.filter($"users" > 10000) // 显示结果 filteredDF.show() // 执行聚合操作 df.createOrReplaceTempView("languages") val resultDF = spark.sql( """ |SELECT language, users |FROM languages |WHERE users > (SELECT AVG(users) FROM languages) |""".stripMargin) resultDF.show() } finally { // 确保SparkSession被正确关闭 spark.stop() } } }5.1 运行配置
- 在IntelliJ中右键点击
SimpleApp对象 - 选择"Run 'SimpleApp'"
- 或创建自定义运行配置:
- Main class:
SimpleApp - VM options:
-Xmx2G -Dlog4j.configurationFile=log4j2.properties - Working directory: 项目根目录
- Environment variables: 可添加
SPARK_LOCAL_IP=127.0.0.1
- Main class:
6. 性能优化配置
在build.sbt中添加以下配置可显著提升本地开发体验:
// 针对本地开发的优化配置 fork in run := true outputStrategy := Some(StdoutOutput) connectInput in run := true // 内存设置 javaOptions in run ++= Seq( "-Xms1G", "-Xmx4G", "-XX:MaxMetaspaceSize=1G", "-XX:+UseG1GC", "-XX:MaxGCPauseMillis=200", "-Djava.net.preferIPv4Stack=true" ) // 针对Spark的特定配置 javaOptions in run ++= Seq( "-Dspark.master=local[*]", "-Dspark.driver.memory=2G", "-Dspark.executor.memory=2G", "-Dspark.ui.enabled=true", "-Dspark.ui.port=4040" )6.1 日志配置示例
在src/main/resources/log4j2.properties中添加:
rootLogger.level = WARN rootLogger.appenderRef.stdout.ref = console appender.console.type = Console appender.console.name = console appender.console.layout.type = PatternLayout appender.console.layout.pattern = %d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n logger.spark.name = org.apache.spark logger.spark.level = WARN logger.jetty.name = org.sparkproject.jetty logger.jetty.level = WARN7. 高级项目配置
7.1 多模块项目结构
对于复杂项目,可以拆分为多个模块:
lazy val core = (project in file("core")) .settings( name := "spark-core", // 核心配置 ) lazy val utils = (project in file("utils")) .settings( name := "spark-utils", // 工具类配置 ) lazy val app = (project in file("app")) .dependsOn(core, utils) .settings( name := "spark-app", // 应用配置 )7.2 打包配置
添加assembly插件用于创建可部署的fat JAR:
- 在
project/plugins.sbt中添加:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")- 在
build.sbt中添加合并策略:
assembly / assemblyMergeStrategy := { case PathList("META-INF", "services", xs @ _*) => MergeStrategy.filterDistinctLines case PathList("META-INF", xs @ _*) => MergeStrategy.discard case "reference.conf" => MergeStrategy.concat case _ => MergeStrategy.first }- 使用命令创建部署包:
sbt assembly8. 实际开发技巧
8.1 使用Spark Shell进行快速验证
在SBT项目中集成Spark Shell功能:
lazy val sparkShell = taskKey[Unit]("Start Spark shell with project dependencies") sparkShell := { val sbtClasspath = (fullClasspath in Runtime).value.files.absString val cmd = s"""spark-shell --jars "$sbtClasspath"""" println(s"Running: $cmd") cmd ! }运行方式:
sbt sparkShell8.2 性能监控配置
在spark-defaults.conf中添加(位于src/main/resources):
spark.eventLog.enabled true spark.eventLog.dir file:///tmp/spark-events spark.history.fs.logDirectory file:///tmp/spark-events spark.metrics.conf.*.sink.console.class org.apache.spark.metrics.sink.ConsoleSink8.3 单元测试示例
在src/test/scala中创建测试类:
import org.apache.spark.sql.SparkSession import org.scalatest.BeforeAndAfterAll import org.scalatest.funsuite.AnyFunSuite class SparkTestExample extends AnyFunSuite with BeforeAndAfterAll { private var spark: SparkSession = _ override def beforeAll(): Unit = { spark = SparkSession.builder() .appName("Testing") .master("local[2]") .config("spark.ui.enabled", "false") .getOrCreate() } override def afterAll(): Unit = { if (spark != null) { spark.stop() } } test("simple dataframe creation") { import spark.implicits._ val data = Seq(("a", 1), ("b", 2)) val df = data.toDF("letter", "number") assert(df.count() == 2) assert(df.columns.length == 2) } }运行测试:
sbt test