ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

IntelliJ IDEA 2020.1 + Maven 3.6.3 + Scala 2.11.8 + Spark 环境搭建实战

IntelliJ IDEA 2020.1 + Maven 3.6.3 + Scala 2.11.8 + Spark 环境搭建实战 1. 为什么这套组合在2020年之后依然值得折腾先把结论摆在前面Intellij idea 2020.1社区版 Maven 3.6.3 Scala 2.11.8 Spark这套组合放到今天看确实有点考古的味道但它依然是很多人入门大数据开发时绕不开的一条路。原因很直接——大量存量项目、教学资料、企业内部的老系统仍然跑在Scala 2.11和Spark 2.x这条线上。你如果一上来就装最新版的Scala 2.13配Spark 3.x遇到老项目直接编译不过回头还得降版本重来白白浪费一整天。我自己第一次搭这套环境的时候踩的坑能写满两页纸。最典型的一个IDEA社区版新建项目时选了Scala SDK结果Maven编译时报object apache is not a member of package org查了半天才发现是Scala版本和Spark依赖的二进制版本对不上。这种问题在教程里往往一笔带过但实际卡住新手的就是这些细节。所以这篇内容我打算按真实搭建顺序来讲不是那种理想化的步骤罗列而是把每一步为什么这么做、哪里容易翻车、翻车了怎么救都摊开说清楚。适合的人群很明确刚接触Spark、需要在Windows本地跑通第一个Scala项目的开发者或者手头有老项目要维护、需要把环境对齐到Scala 2.11这条线的同学。整套流程走下来顺利的话两三个小时能跑通第一个WordCount不顺利的话可能要在版本兼容上耗掉半天这篇就是帮你把后面那半天省下来。需要提前说明的是Windows平台搭这套环境最大的敌人不是技术难度而是路径、编码、版本这三件事。下面每个章节我都会围绕这三条主线展开。2. 动手之前先把四个组件的版本关系理清楚很多人搭环境失败根子不在操作而在于没搞明白这几个组件之间到底谁依赖谁。我见过太多人把JDK、Scala、Spark、Maven当成四个独立的东西分别装最新版结果凑一起就炸。所以这一章先把关系讲透后面操作才不会盲目。2.1 JDK、Scala、Spark三者的版本咬合关系先记住一条铁律Spark的每个大版本都绑定了特定的Scala二进制版本。Spark 2.4.x默认编译用的是Scala 2.11Spark 3.0之后才转向Scala 2.12。你项目里写的Scala代码最终要被编译成字节码而这个字节码必须和Spark运行时依赖的Scala版本二进制兼容。版本对不上轻则编译警告重则运行时抛NoSuchMethodError而且这种错误往往在运行到某个特定算子时才爆出来极难排查。JDK这边Scala 2.11.8对JDK 8的支持是最稳的。JDK 11虽然也能跑但Scala 2.11的编译器在JDK 11下偶尔会有反射相关的警告而且Spark 2.x对JDK 11的支持并不完整。所以我的建议很明确JDK锁死8别贪新。这不是保守是省事。组件推荐版本为什么是这个版本JDK1.88u系列Scala 2.11.8与Spark 2.x在JDK 8下兼容性最好Scala2.11.8与Spark 2.4.x的二进制版本严格对应Maven3.6.3稳定且与IDEA 2020.1的集成成熟Spark2.4.x与Scala 2.11.8配套的最后一个稳定大版本2.2 为什么Maven版本也不能随便选Maven 3.6.3这个版本不是随便定的。IDEA 2020.1内置的Maven集成对3.6.x系列支持最顺3.8之后Maven改了一些默认行为比如默认阻止HTTP仓库在老项目里反而容易出问题。而且3.6.3的依赖解析速度和对settings.xml镜像配置的处理都比较成熟。另一个容易被忽略的点Maven的本地仓库路径。默认在C:\Users\你的用户名\.m2\repository如果你的C盘空间紧张或者用户名带中文强烈建议在settings.xml里改到一个纯英文、无空格的路径。中文路径在Windows下引发的诡异问题我后面会专门讲。2.3 环境变量配置里最容易写错的两个地方环境变量这块网上教程千篇一律但有两个细节几乎没人强调第一JAVA_HOME指向的路径不要带\bin。很多人复制路径时顺手把bin也带上了结果Maven和Scala都找不到JDK。正确写法是JAVA_HOME C:\Java\jdk1.8.0_xxx然后Path里加%JAVA_HOME%\bin。第二SCALA_HOME和MAVEN_HOME同理都指向解压后的根目录不带bin。配置完之后一定要新开一个命令行窗口验证因为环境变量的更新不会影响已经打开的窗口。这个坑我踩过不止一次明明配好了却一直报不是内部或外部命令就是因为用的是旧窗口。验证命令很简单java -version scala -version mvn -version三条都能正常输出版本号且java -version显示的是1.8这一步才算过关。如果scala -version报错八成是Path没配对如果mvn -version里显示的Java版本不对说明JAVA_HOME有问题。3. 从零到跑通第一个Scala项目的完整操作链路这一章是重头戏我按实际操作顺序一步步来每一步都标注了为什么这么做和这里容易出什么问题。你照着走基本不会偏。3.1 IDEA社区版的一个先天限制及应对思路先说个扎心的事实Intellij idea社区版没有内置的Scala插件支持而旗舰版才有完整的Scala和Spark支持。但社区版不是不能用只是需要手动装Scala插件。这是很多人卡在第一步的原因——新建项目时找不到Scala选项以为社区版不支持其实只是插件没装。操作路径File - Settings - Plugins在Marketplace里搜Scala安装后重启IDEA。注意2020.1这个版本对应的Scala插件版本也要匹配如果装最新版插件报兼容性错误就去插件页面找历史版本选一个标注支持2020.1的。装完插件后新建项目时就能看到Scala选项了。但这里有个选择是建Maven项目再手动加Scala支持还是直接建Scala项目我的建议是建Maven项目因为Spark项目本质上依赖管理靠Maven直接建Scala项目反而要额外配Maven绕远了。3.2 新建Maven项目时的坐标填写与骨架选择新建项目选MavenJDK选你装好的1.8。到了填GroupId和ArtifactId这一步随便填个有意义的名字就行比如com.demo和spark-demo。关键是不要勾选Create from archetype也就是不要用模板骨架。原因很简单Scala项目的目录结构和Java不一样用Java的archetype生成出来的目录还得手动改不如从空项目开始自己建目录。项目建好后默认目录是src/main/java我们要手动改成Scala的规范结构src/main/scala src/main/resources src/test/scala在IDEA里右键src/main新建Directory输入scala即可。然后关键一步把这个scala目录标记为Sources Root。右键目录 -Mark Directory as-Sources Root目录会变蓝。这一步不做IDEA不会把里面的Scala文件当源码编译后面会报找不到类。3.3 pom.xml里必须写对的几段配置pom.xml是整个项目的灵魂写错了后面全是坑。我直接给一份经过验证的配置然后逐段解释。properties maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target scala.version2.11.8/scala.version spark.version2.4.5/spark.version encodingUTF-8/encoding /propertiesscala.version和spark.version这两个属性一定要显式声明后面依赖里用${}引用改版本时只改一处避免遗漏。依赖部分dependencies dependency groupIdorg.scala-lang/groupId artifactIdscala-library/artifactId version${scala.version}/version /dependency dependency groupIdorg.apache.spark/groupId artifactIdspark-core_2.11/artifactId version${spark.version}/version /dependency dependency groupIdorg.apache.spark/groupId artifactIdspark-sql_2.11/artifactId version${spark.version}/version /dependency /dependencies注意spark-core_2.11这个后缀_2.11就是Scala的二进制版本标识。如果你写成spark-core_2.12而你的Scala是2.11.8编译能过但运行必炸。这个后缀是Maven坐标的一部分不是可有可无的装饰。构建插件部分需要scala-maven-pluginbuild plugins plugin groupIdnet.alchim31.maven/groupId artifactIdscala-maven-plugin/artifactId version3.4.6/version executions execution goals goalcompile/goal goaltestCompile/goal /goals /execution /executions /plugin /plugins /build这个插件的作用是让Maven能编译Scala代码。没有它Maven只认Java你的.scala文件会被直接忽略编译时一个类都找不到。3.4 第一个Scala文件的编写与运行验证在src/main/scala下新建一个Scala Object写个最简单的WordCountimport org.apache.spark.{SparkConf, SparkContext} object WordCount { def main(args: Array[String]): Unit { val conf new SparkConf().setAppName(WordCount).setMaster(local[*]) val sc new SparkContext(conf) val data sc.parallelize(Seq(hello spark, hello scala, spark is fast)) val result data.flatMap(_.split( )).map((_, 1)).reduceByKey(_ _) result.collect().foreach(println) sc.stop() } }setMaster(local[*])表示用本地所有CPU核心跑这是本地开发的标准写法。跑起来如果能看到每个单词的计数输出恭喜你环境通了。但这里有个Windows特有的坑运行时会报Could not locate executable null\bin\winutils.exe。这个错误不影响结果但看着烦。原因是Spark在Windows下需要Hadoop的winutils工具而Windows没有。解决办法是下载对应Hadoop版本的winutils放到一个目录然后配置HADOOP_HOME环境变量指向它。或者更简单在代码里加一行System.setProperty(hadoop.home.dir, 你的路径)。这个坑几乎每个Windows用户都会遇到提前知道能省不少搜索时间。4. 那些让新手抓狂的报错与排查思路环境搭建最耗时的从来不是顺利的部分而是报错。这一章我把几个高频报错的完整排查链路写出来你可以对照着复现排查思路而不是直接抄答案。4.1 依赖下载卡住或失败的几种成因Maven下载依赖慢是新手遇到的第一个劝退点。默认从中央仓库拉国内网络环境下经常卡在某个jar上不动。解决办法是配置国内镜像。在settings.xml的mirrors里加一段镜像配置指向国内的Maven镜像源。配置完之后记得在IDEA里指定这个settings.xmlSettings - Build Tools - Maven把User settings file指向你改过的那个文件。但镜像也不是万能的。有时候某个依赖在镜像里没有会报Could not find artifact。这时候先别急着换镜像先确认你的坐标写对了没有——groupId、artifactId、version三个字段任何一个拼错都会导致找不到。我见过有人把spark-core写成sparkcore找了半天。还有一种情况是依赖下载到一半断了本地仓库里留了个.lastUpdated文件导致Maven认为这个依赖已经尝试过、不再重试。解决办法是删掉对应目录下的.lastUpdated文件或者直接删掉整个依赖目录重新拉。4.2 Scala版本不匹配引发的编译错误长什么样版本不匹配的报错有个典型特征编译期可能不报错运行期才炸。比如你Scala用2.12Spark依赖用_2.11编译时scala-maven-plugin可能只是警告但运行时一调用Spark的类就抛NoSuchMethodError或ClassNotFoundException。排查这类问题的思路是先看报错里提到的类属于哪个包然后去Maven依赖树里查这个包是哪个版本。IDEA右侧的Maven面板有个Dependencies视图能看到完整的依赖树。如果发现同一个包有多个版本那就是版本冲突需要用exclusions排除掉多余的。更隐蔽的一种Scala SDK在IDEA里的配置和pom.xml里的版本不一致。IDEA的Project Structure - Global Libraries里可能挂着一个Scala 2.12的SDK而pom里写的是2.11.8。这种情况下IDEA的代码提示按2.12走Maven编译按2.11走两边打架。解决办法是统一要么在IDEA里把Scala SDK换成2.11.8要么干脆不挂全局SDK完全靠Maven管理。4.3 中文乱码与路径空格的隐藏陷阱Windows下有两个隐形杀手中文路径和路径空格。中文路径的问题在于某些工具链在处理路径时用的编码不一致导致读不到文件。比如你的项目放在D:\我的项目\spark-demoMaven或Scala编译器可能就找不到源码。解决办法很简单所有和开发相关的路径全部用纯英文、无空格。项目路径、Maven仓库路径、JDK安装路径一个都别例外。路径空格的问题类似。C:\Program Files\Java这种带空格的路径在某些脚本里会被截断。所以JDK、Scala、Maven这些工具我建议统一装在C:\dev\这类简短无空格的目录下。中文乱码则主要出现在控制台输出。IDEA的控制台默认编码可能和系统不一致导致Scala程序里的中文输出变成乱码。解决办法是在IDEA的Settings - Editor - File Encodings里把Global Encoding、Project Encoding、Default encoding for properties files全部设为UTF-8并勾选Transparent native-to-ascii conversion。同时在pom.xml里加上project.build.sourceEncodingUTF-8/project.build.sourceEncoding。4.4 一个完整的排查案例复盘说个我实际遇到的案例。某次帮人看环境现象是代码能编译但一运行就报java.lang.NoClassDefFoundError: scala/Product。这个报错的意思是运行时找不到Scala的基础类。排查链路是这样的第一步确认pom里有没有scala-library依赖——有。第二步看依赖树里scala-library的版本——发现是2.12但pom里明明写的2.11.8。第三步追查为什么变成2.12——原来是spark-sql的某个传递依赖带进来了2.12的scala-library覆盖了显式声明的版本。解决办法是在spark-sql依赖里加exclusions把传递进来的scala-library排除掉强制用我们声明的2.11.8。这个案例说明依赖冲突不一定来自你直接写的依赖更多时候来自传递依赖。学会看依赖树是排查这类问题的基本功。5. 让开发体验更顺手的几个配置调整环境跑通只是及格线真正提升效率的是那些顺手的配置。这一章分享几个我用了很久、确实能省时间的调整。5.1 IDEA里Scala编译器的性能调优Scala编译慢是出了名的尤其是项目大了之后。IDEA里可以调整编译器的堆内存。在Settings - Build, Execution, Deployment - Compiler里把Build process heap size从默认的700调到2048甚至更高。这个调整对编译速度的提升是肉眼可见的尤其是依赖多的时候。另外Scala插件的Compile server选项建议开启。它会让编译在一个独立的进程里跑避免每次编译都重新加载第二次之后的编译速度会快很多。开启路径在Settings - Languages Frameworks - Scala里。5.2 Maven的离线模式与依赖缓存策略依赖都下载完之后日常开发其实不需要每次都联网检查更新。IDEA的Maven面板里有个Toggle Offline Mode按钮开启后Maven只用本地仓库不再联网。这在网络不稳定的时候特别有用能避免因为网络问题导致的构建失败。但要注意离线模式下如果本地缺某个依赖会直接报错而不是去下载。所以开启离线模式的前提是所有依赖都已经完整下载到本地。我一般是在项目第一次完整构建成功后再开离线模式。5.3 日志输出太吵怎么办Spark默认会打一大堆INFO级别的日志控制台刷得飞快真正有用的信息反而被淹没。解决办法是在src/main/resources下放一个log4j.properties文件把日志级别调到WARNlog4j.rootCategoryWARN, console log4j.appender.consoleorg.apache.log4j.ConsoleAppender log4j.appender.console.targetSystem.err log4j.appender.console.layoutorg.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n这样控制台就清爽多了只输出警告和错误以及你自己println的内容。这个配置虽小但对调试体验的提升很大。5.4 打包提交时的注意事项本地跑通之后下一步往往是打包成jar提交到集群。这里有个关键点Spark相关的依赖不要打进jar包。因为集群上已经有Spark环境了你打进去反而会造成冲突。在pom里把spark-core、spark-sql这些依赖的scope设为provideddependency groupIdorg.apache.spark/groupId artifactIdspark-core_2.11/artifactId version${spark.version}/version scopeprovided/scope /dependencyprovided的意思是编译时需要、打包时不带。这样打出来的jar体积小提交到集群也不会和集群自带的Spark冲突。这个细节很多人第一次提交作业时都会忽略导致jar包几百兆或者运行时类冲突。6. 关于这套环境的一些个人体会搭这套环境的过程本质上是在和版本兼容这四个字较劲。我自己的经验是把版本关系当成一张网来看而不是一条线。JDK、Scala、Spark、Maven任意两个之间都有兼容性约束牵一发而动全身。所以每次要升级其中任何一个都要先确认其他三个是否还兼容。另一个体会是Windows平台做大数据开发确实比Linux麻烦主要就麻烦在路径、编码、缺少原生工具这几件事上。但本地开发用Windows、提交运行用Linux这个组合其实挺常见把Windows这边的坑趟平了日常开发效率并不低。最后分享一个小习惯每搭好一套环境就把pom.xml和settings.xml备份一份。下次换机器或者重装系统直接拿过来用能省掉大量重复劳动。环境搭建这种事做一次就够了没必要每次都从零开始。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表