一、上次课回顾
二、IDEA整合Maven搭建Spark开发环境
三、词频统计案例开发及上传jar包到服务器并准备测试数据
四、提交Spark应用程序运行
五、词频统计案例迭代之输出结果到HDFS
六、词频统计案例迭代之处理多个输入文件
七、词频统计案例之输入文件规则匹配
八、带排序的词频统计案例开发及执行过程剖析
九、带排序的词频统计案例spark-shell快速测试
##
一、上次课回顾
上次课博客总结:
1、若泽数据B站视频Spark05 - Spark-RDD的基本操作(一)
2、若泽数据B站视频Spark06 - Spark-RDD的基本操作(二)
思考:
- 我们使用Maven进行项目的管理,拷贝jar包遇到各种冲突怎么办?
项目实战中Hadoop综合Phoenix、Hbase,保守几百个jar包,冲突解决不了,so使用Maven管理项目。
二、IDEA+Maven来构建我们的Spark Application
- 点击file->new->project,点击create from archetype;选择org.scala-tools.archetypes:scala-archetype-simple;
- 单机下一步GroupId:com.ruozedata.bigdata、ArtifactId:g6-spark;
- 选定maven_home 目录、使用maven_home下的settings.xml、再选择local_repository(这个是settings.xml中加的自动识别出来的)。
两个地方查看引入的Jar包:
1、External Libraries
2、View --> Tool Windows --> Maven Projects,点击项目名称,再点击Dependencies
在pom.xml文件中需要添加如下:
1、除自带的repository以外还需要添加一个,它默认的repository是访问不到的。
<repository>
<id>cloudera</id>
<name>cloudera</name>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
2、添加dependency依赖文件
<!--添加spark-core依赖-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!--添加Hadoop.version的依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
注意的是: 在properties文件下做出如下修改,便于后期对版本进行维护。
<properties>
<scala.version>2.11.8</scala.version>
<spark.version>2.4.0</spark.version>
<hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
</properties>
在pom.xml中,右键maven,点击reimport,重跑依赖包;再点击view->Tool Windows->Maven Projects,点开lifecycle,点击clean。再等一会就不会报错了。
此处注意点:
1)更换scala版本
2)添加Spark-Core依赖
3)添加hadoop-client依赖
4)添加cdh仓库
三、词频统计案例开发及上传jar包到服务器并准备测试数据
1、快速的在IDEA下写一个WordCount程序:
新建一个WordCountApp:
package com.ruozedata.spark
import org.apache.spark.{SparkConf, SparkContext}
object WordCountApp {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf()
val sc = new SparkContext(sparkConf)
val textFile = sc.textFile(args(0))
val wc = textFile.flatMap(line => line.split("\t"))
.map((_,1)).reduceByKey(_+_)
wc.collect.foreach(println)
sc.stop()
}
}
2、WordCountApp开发完成后,进入Maven Projects ==> Lifecycle ==> Run Maven Build,进行打包,打包完成后会有如下提示包的位置:
Building jar: G:\ruozedata_workspace\sparktrain\target\spark-train-1.0.jar
3、通过命令上传至服务器
[hadoop@hadoop004 lib]$ pwd
/home/hadoop/lib
[hadoop@hadoop004 lib]$ ll
total 16
-rw-r--r-- 1 hadoop hadoop 2217 Jul 31 2019 spark-train-1.0.jar
小插曲:如何上传本地的文件至服务器?
生产上一般使用rz上传,ftp工具在生产上是不用的??
生产上都是跳板机,先登录跳板机,再从跳板机到服务器,
中间还有一层堡垒机,ftp服务是绝对连接不上去的,ftp在生产上是不okay的。
我们生产上使用rz肯定是没有问题的,rz不用经过堡垒机。
4、如何在hdfs上使用
hdfs数据准备:hdfs dfs -mkdir /wordcount/input/
hdfs dfs -put /home/hadoop/ruozeinput.txt /wordcount/input/
四、提交Spark应用程序运行
详见:http://spark.apache.org/docs/latest/submitting-applications.html
Once a User application is bundled, it can be launched using the bin/spark-submit script. This script takes care of setting up the classpath with Spark and its dependencies, and can support different cluster manages and deploy modes that Spark supports.
译:一旦一个应用程序被构建完成,它能够使用bin/spark-submit脚本;这个脚本使用它的classpath和依赖项,它能够支持不同的集群管理和部署模式。
1、如何在hdfs上使用:
hdfs数据准备:hdfs dfs -mkdir /wordcount/input/
hdfs dfs -put /home/hadoop/ruozeinput.txt /wordcount/input/
[hadoop@hadoop002 bin]$ ./spark-submit --help
Usage: spark-submit [options] <app jar | python file | R file> [app arguments]
//[options] 可以选择的选项,application jar或者python file或者R file,[app argumnets]这个又是可选的。
Usage: spark-submit --kill [submission ID] --master [spark://...] //干掉进程
Usage: spark-submit --status [submission ID] --master [spark://...] //查看状态
Usage: spark-submit run-example [options] example-class [example args] //运行案例
Options:
--master Master URL spark://host:port, mesos://host:port,yarn,or local (default:local[*])
//可以指定master的URL
--deploy-mode DEPLOY_MODE whether to launch the driver program locally ("client") or on one of the worker machines inside the cluster ("cluster")
//部署模式,有client和cluster模式。
--class CLASS_NAME Your application's main class (for java / scala apps) //你的应用程序的主类仅仅是对于Java、scala文件来说的。
--name Name A name of your application //指定spark的作业名称,可以在UI上看到
--jars JARS Comma-seprated list of maven coordinates of jars to include on the driver and executor classpaths.
最主要的执行片段:
./bin/spark-submit \
--class <main-class> \
--depoly-mode <deploy-mode> \
--conf <key>=<values> \
........# other options
<application-jar> \
[application-arguments]
执行代码:
运行时把 \ 去掉,并在一行输入:
1、通过hdfs访问:
[hadoop@hadoop002 spark-2.4.2-bin-2.6.0-cdh5.7.0]$ ./bin/spark-submit \
--class com.ruozedata.spark.WordCountApp \
--master local[2] \
/home/hadoop/lib/spark-train-1.0.jar \
hdfs://10.0.0.132:9000/wordcount/input/ruozeinput.txt
--------------------------------------------------------------------
注意:Hadoop通过路径和链接访问hdfs:
10.0.0.132:9000同下面的<value></value>中的内容
hdfs://10.0.0.132:9000/wordcount/input/
[hadoop@hadoop002 hadoop]$ cat core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!--Yarn 需要使用 fs.defaultFS 指定NameNode URI -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://10.0.0.132:9000</value>
</property>
</configuration>
2019-07-31执行spark-submit时报错,发现打包的代码写在test类下面哈哈,所以提示java.lang.ClassNotFoundException
五、词频统计案例迭代之输出结果到HDFS
承接4,输出在控制台上一点用都没有
1、修改4中的代码:
wc.collect().foreach(println) ==> wc.saveAsTextFile(args(1))
重新打包编译上传至服务器
2、我们查看源码中saveAsTextFile方法:
概念:把RDD的结果存储到一个路径,我们指定为hdfs上的路径:hdfs://10.0.0.132:9000/wordcount/output4
3、命令测试:
./bin/spark-submit --class com.ruozedata.spark.WordCountApp --master local[2] /home/hadoop/lib/spark-train-1.0.jar hdfs://10.0.0.132:9000/wordcount/input/ruozeinput.txt hdfs://10.0.0.132:9000/wordcount/output4/
4、去到hdfs上查看文件:
19/07/23 22:36:12 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 3 items
-rw-r--r-- 1 hadoop supergroup 0 2019-07-23 22:35 /wordcount/output4/_SUCCESS
-rw-r--r-- 1 hadoop supergroup 60 2019-07-23 22:35 /wordcount/output4/part-00000.bz2
-rw-r--r-- 1 hadoop supergroup 50 2019-07-23 22:35 /wordcount/output4/part-00001.bz2
执行过程中要关注控制台跳出的信息:
19/07/23 22:35:32 INFO mapred.FileInputFormat: Total input paths to process : 1
==>代表的是输入的文件数.
六、词频统计案例迭代之处理多个输入文件
多传几个文件进入/wordcount/input/ 目录下
1、此时hdfs目录下/wordcount/input/ 目录下有多个文件
hdfs dfs -put /home/hadoop/data/ruozeinput.txt /wordcount/input/2
hdfs dfs -put /home/hadoop/data/ruozeinput.txt /wordcount/input/3
2、再次执行命令:
./bin/spark-submit --class com.ruozedata.spark.WordCountApp --master local[2] /home/hadoop/lib/spark-train-1.0.jar hdfs://10.0.0.132:9000/wordcount/input/* hdfs://10.0.0.132:9000/wordcount/output5/
此时控制台上:Total input paths to process : 3 //因为输入文件是3个
3、hdfs上查看执行结果:
[hadoop@hadoop002 lib]$ hdfs dfs -text /wordcount/output5/*
19/07/23 22:50:24 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
19/07/23 22:50:25 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(world,6)
19/07/23 22:50:25 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(hello,9)
19/07/23 22:50:25 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(john,3)
七、词频统计案例之输入文件规则匹配
此时hdfs上/wordcount/input下有好几个文件
1、此时有3个文件:
[hadoop@hadoop002 lib]$ hdfs dfs -ls /wordcount/input/
19/07/23 22:57:51 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Found 3 items
-rw-r--r-- 1 hadoop supergroup 35 2019-07-23 22:47 /wordcount/input/1
-rw-r--r-- 1 hadoop supergroup 35 2019-07-23 22:47 /wordcount/input/2
-rw-r--r-- 1 hadoop supergroup 35 2019-07-20 05:46 /wordcount/input/ruozeinput.txt
2、spark-submit支持通配符:
输入文件中:hdfs://10.0.0.132:9000/wordcount/input/* .txt //它就自动会去找/wordcount/input/目录下以.txt结尾的文件.
otal input paths to process : 1 //因为输入文件中只有一个文件结尾是.txt
八、带排序的词频统计案例开发及执行过程剖析
1、再次修改源码:
// wc.collect().foreach(println)\
val SortTest = wc.map(x => (x._2,x._1)).sortByKey(false).map(x =>(x._2,x._1)
SortTest.saveAsTextFile(args(1))
2、再次打包上传进行测试(注意更换输出目录避免重复):
./bin/spark-submit --class com.ruozedata.spark.WordCountApp --master local[2] /home/hadoop/lib/spark-train-1.0.jar hdfs://10.0.0.132:9000/wordcount/input/* hdfs://10.0.0.132:9000/wordcount/output6/
3、读取出/wordcount/output6/目录下内容,证明降序排列成功:
[hadoop@hadoop002 spark-2.4.2-bin-2.6.0-cdh5.7.0]$ hdfs dfs -text /wordcount/output7/*
19/07/23 23:17:06 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
19/07/23 23:17:07 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(hello,9)
19/07/23 23:17:07 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(world,6)
19/07/23 23:17:07 INFO compress.CodecPool: Got brand-new decompressor [.bz2]
(john,3)
4、成功了,Are You Okay
5、升序排列:val SortTest = wc.map(x => (x._2,x._1)).sortByKey(true).map(x =>(x._2,x._1)
把false ==> true
九、带排序的词频统计案例spark-shell快速测试
1、val textFile = sc.textFile("hdfs://10.0.0.132:9000/wordcount/input/*.txt")
textFile: org.apache.spark.rdd.RDD[String] = hdfs://10.0.0.132:9000/wordcount/input/*.txt MapPartitionsRDD[7] at textFile at <console>:24
2、scala> val wc = textFile.flatMap(line => line.split("\t")).map((_,1)).reduceByKey(_+_)
wc: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[10] at reduceByKey at <console>:25
3、scala> wc.map(x =>(x._2,x._1)).collect
res10: Array[(Int, String)] = Array((3,hello), (2,world), (1,john))
4、scala> wc.map(x => (x._2,x._1)).sortByKey(false).collect
res12: Array[(Int, String)] = Array((3,hello), (2,world), (1,john))
问题:sortByKey中有两个默认参数,如果没有参数,括号可以省略;如果声明的是默认参数,括号不能省略。
sortByKey方法在源码中定义:
sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)