WordCount
是一个快速入门案例,单词统计,通过此案例,学习如何用 scala
来编写 spark
程序,spark
支持 java
,scalal
这些语言,目前在企业中大部分公司都是使用 scala
进行开发,后序的 flink
是基于 java
开发的,这与官网的引导有关,flink
的源码在去scala
化 ,基于此,将要实现以下几个目标:
idea
spark-submit
spark-shell
Spark historyServer
配置前置文章请参考:
本程序 基于 spark 3.2.4
,scala 2.12.x
版本开发
由于后续任务运行时,需要在 客户端节点或hadoop节点机器上
操作,文件来源,使用 hdfs
hello.txt
上传至 hdfs
如下操作
[root@hadoop01 data]# hdfs dfs -put hello.txt /tmp/
2023-11-02 09:23:45,159 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop01 data]# ls
hadoop_repo hello.txt hive_repo soft
编码之前,要清楚代码的逻辑,即加载的数据如何一步一步变换成最终想要的结果 ,由下分析步骤可知,万变不离其宗 S -> T -> S
三板斧, 详细请参考 Spark的工作与架构原理
S
)T
)T
)T
)S
)代码如下
object WordCount {
def main(args: Array[String]): Unit = {
// "hdfs:///tmp/hello.txt"
var path = "/Users/hyl/Desktop/fun/sts/spark-demo/hello.txt"
if(args.length==1){
path = args(0)
}
// 第一步:创建SparkContext
val conf = new SparkConf()
conf
// 设置任务名称
.setAppName("WordCount")
// local 表示本地运行
.setMaster("local")
val sp = new SparkContext(conf)
// 第二步:加载数据
val lineRdd = sp.textFile(path)
// 第三步:对数据进行分隔,将一行数据分隔成一个一个的单词
val wordsRdd = lineRdd.flatMap(_.split(" "))
// 第四步:迭代 words,将第个 word 转化为 (word,1) 这种形式
val pairRdd = wordsRdd.map((_, 1))
// 第五步:根据 key (其实就是 word) 进行分组聚合统计
val wordCountRdd = pairRdd.reduceByKey(_ + _)
// 第六步:结果打印
wordCountRdd.foreach(println _)
}
}
idea
运行 idea
得如下结果
来体验一下 scala
函数式编程的极简之美
object WordCount2 {
def main(args: Array[String]): Unit = {
var path = "/Users/hyl/Desktop/fun/sts/spark-demo/hello.txt"
if (args.length == 1) {
path = args(0)
}
val conf = new SparkConf()
conf
// 设置任务名称
.setAppName("WordCount")
// local 表示本地运行
.setMaster("local")
new SparkContext(conf).textFile(path).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).foreach(println _)
}
}
spark-submit
使用 spark-submit
提交到集群执行,实际工作中会使用这种方式
那接下来需要把代码提交到集群中去执行
这个时候就需要对代码打包了
首先在项目的 pom
文件中添加 build
配置,和 dependencies
标签平级,详细请参考 源码
注意: 打包时,要将如下图的代码注释掉
[root@hadoop01 jar]# ls
spark-demo-1.0-SNAPSHOT.jar
[root@hadoop01 jar]# pwd
/data/jar
spark-submit \
--class com.fun.scala.WordCount2 \
--master yarn \
--deploy-mode client \
--executor-memory 1G \
--num-executors 1 \
/data/jar/spark-demo-1.0-SNAPSHOT.jar \
hdfs:///tmp/hello.txt
Exception in thread “main” org.apache.spark.SparkException: When running with master ‘yarn’ either HADOOP_CONF_DIR or YARN_CONF_DIR must be set in the environment.
[root@hadoop01 bin]# vi /etc/profile
export HADOOP_CONF_DIR=/data/soft/hadoop-3.2.4/etc/hadoop/
[root@hadoop01 bin]# source /etc/profile
[root@hadoop01 bin]#
spark-shell
这种方式方便在集群环境中调试代码
有一些代码对环境没有特殊依赖的时候可以直接使用第一种方式,在idea
中调试代码
但是有时候代码需要依赖线上的一些环境,例如:需要依赖线上的数据库中的数据,由于权限问题,在本地是无法连接的
这个时候想要调试代码的话,可以选择使用spark-shell
的方式,直接在线上服务器中开启一个spark
的交互式命令行窗口
注意:使用
spark-shell
的时候,也可以选择指定开启本地spark
集群,或者连接standalone
集群,或者使用on yarn
模式,都是可以的
[root@hadoop01 bin]# spark-shell
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
2023-11-02 10:20:11,746 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Spark context Web UI available at http://hadoop01:4040
Spark context available as 'sc' (master = local[*], app id = local-1698891612526).
Spark session available as 'spark'.
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/___/ .__/\_,_/_/ /_/\_\ version 3.2.4
/_/
Using Scala version 2.12.15 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_391)
Type in expressions to have them evaluated.
Type :help for more information.
scala>
执行需要的代码
scala> val path = "hdfs:///tmp/hello.txt"
path: String = hdfs:///tmp/hello.txt
scala> sc.textFile(path).flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _).foreach(println _)
(hyl,1)0:> (0 + 2) / 2]
(word,1)
(hello,4)
(12755167,1)
(test,1)
scala>
Spark historyServer
配置请先配置 hadoop historyServer
刚才使用 on yarn
模式的时候会发现看不到输出的日志信息,这主要是因为没有开启 spark
的historyserver
,只开启了hadoop
的historyserver
需要修改spark-defaults.conf
和spark-env.sh
首先对spark-defaults.conf.template
重命名
然后在spark-defaults.conf
中增加以下内容
spark.eventLog.enabled=true
spark.eventLog.compress=true
spark.eventLog.dir=hdfs:///tmp/logs/root/logs
spark.history.fs.logDirectory=hdfs:///tmp/logs/root/logs
spark.yarn.historyServer.address=http://hadoop01:18080
注意:在哪个节点上启动spark的
historyserver
进程,spark.yarn.historyServer.address
的值里面就指定哪个节点的主机名信息
在spark-env.sh
中增加以下内容
export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=18080 -Dspark.history.fs.logDirectory=hdfs:///tmp/logs/root/logs"
java.io.FileNotFoundException: File does not exist: hdfs:/tmp/logs/root/logs
[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# hdfs dfs -mkdir hdfs:/tmp/logs/root/logs
2023-11-02 10:36:47,136 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# sbin/start-history-server.sh
starting org.apache.spark.deploy.history.HistoryServer, logging to /data/soft/spark-3.2.4-bin-hadoop3.2/logs/spark-root-org.apache.spark.deploy.history.HistoryServer-1-hadoop01.out
[root@hadoop01 spark-3.2.4-bin-hadoop3.2]# jps
11716 NameNode
2628 Jps
12117 SecondaryNameNode
12503 ResourceManager
10520 Master
11243 RunJar
2333 HistoryServer
在 yarn
上重新执行 WorkCount
程序,可以看到以下输出内容
至此 WordCount
程序开发及运行结束,如有问题,欢迎评论区留言。