1 环境搭建
1.1 使用maven创建spark项目
如上图所示点击next创建自己的spark项目;
对maven进行修改
1.2 修改pom.xml
<!--依赖的版本-->
<properties>
<scala.version>2.11.8</scala.version>
<spark.version>2.2.0</spark.version>
<hadoop.version>2.6.0-cdh5.7.0</hadoop.version>
</properties>
<!--jar包下载的创库-->
<repositories>
<repository>
<id>cloudera</id>
<name>cloudera</name>
<url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
</repository>
</repositories>
<!--scala depedency-->
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<!--spark core depedency-->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>${spark.version}</version>
</dependency>
<!--hadoop-client depedency-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
2 词频统计代码开发
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by grace on 2018/4/27.
*/
object WordCountApp {
def main(args: Array[String]): Unit = {
//创建一个SparkConf
val conf=new SparkConf();
//sparkContext中传递一个SparkConf
val sc=new SparkContext(conf);
//接受一个文件
val textFile=sc.textFile(args(0));
//词频统计
// (这里注意了,自己的文件是按照什么切分的,
//作者就因为文件中的单词按照空格切分,代码中按照“\t”切分了,一直得不到想要的结果)
val wc=textFile.flatMap(line =>line.split(" ")).map(x=>(x,1)).reduceByKey(_+_);
//控制台输出结果
wc.collect().foreach(println)
//把结果存入到hdfs上
//saveAsTextFile这个方法可以传递codec选择压缩方式进行存储
wc.saveAsTextFile(args(1))
//关闭
sc.stop();
}
}
注意: saveAsTextFile这个方法
/**
* Save this RDD as a compressed text file, using string representations of elements.
*/
def saveAsTextFile(path: String, codec: Class[_ <: CompressionCodec]): Unit = withScope {
// https://issues.apache.org/jira/browse/SPARK-2075
val nullWritableClassTag = implicitly[ClassTag[NullWritable]]
val textClassTag = implicitly[ClassTag[Text]]
val r = this.mapPartitions { iter =>
val text = new Text()
iter.map { x =>
text.set(x.toString)
(NullWritable.get(), text)
}
}
RDD.rddToPairRDDFunctions(r)(nullWritableClassTag, textClassTag, null)
.saveAsHadoopFile[TextOutputFormat[NullWritable, Text]](path, codec)
}
通过源码我们可以看出saveAsTextFile这个方法可以传递codec选择压缩方式进行存储def saveAsTextFile(path: String, codec: Class[_ <: CompressionCodec])
可以传递两个参数:保存路径和压缩格式,大家可以自己测试下。
3 打包上传
点击package运行打包成功可以再target陌路下找到该jar包,这时候就可以上传了
4 提交应用程序(spark-submit)
**注意:**这里作者出现了几个错误,详见该博客
- 输入文件
[hadoop@hadoop data]$ cat input.txt
hello java
hello hadoop
hello hive
hello sqoop
hello hdfs
hello spark
- 任务提交
如果没有配置`SPARK_HOME`需要再bin目录下提交
spark-submit \
--class cn.zhangyu.WordCountApp \
--master local[2] \
/home/hadoop/lib/wordcount-1.0-SNAPSHOT.jar \
/input /output
其中/input为输入文件的目录(hdfs上),不存在会报错
/output为输出文件的目录
如果不行修改成:
./spark-submit \
--class cn.zhangyu.WordCountApp \
/home/hadoop/lib/spark_test-1.0.jar \
hdfs://主机名或者ip:端口号/输入文件目录 hdfs://主机名或者ip:端口号/输出文件目录
这个端口号是配置再core-site.xml中的。
- 控制台输出结果
(hive,1)
(hello,6)
(java,1)
(sqoop,1)
(spark,1)
(hadoop,1)
(hdfs,1)
- hdfs输出结果
[hadoop@hadoop data]$ hdfs dfs -ls /output
Found 3 items
-rw-r--r-- 3 hadoop supergroup 0 2018-04-21 03:54 /output/_SUCCESS
-rw-r--r-- 3 hadoop supergroup 39 2018-04-21 03:54 /output/part-00000.deflate
-rw-r--r-- 3 hadoop supergroup 31 2018-04-21 03:54 /output/part-00001.deflate
[hadoop@hadoop data]$ hdfs dfs -text /output/part-00000.deflate
18/04/21 03:57:17 INFO zlib.ZlibFactory: Successfully loaded & initialized native-zlib library
18/04/21 03:57:17 INFO compress.CodecPool: Got brand-new decompressor [.deflate]
(hive,1)
(hello,6)
(java,1)
(sqoop,1)
注意:
18/04/21 03:54:07 INFO FileInputFormat: Total input paths to process : 1
大家在日志中会发现这句话,先猜测下是什么意思呢?
5处理多个文件
这里又上传了四个文件
[hadoop@hadoop data]$ hdfs dfs -put input.txt /input/1
[hadoop@hadoop data]$ hdfs dfs -put input.txt /input/2
[hadoop@hadoop data]$ hdfs dfs -put input.txt /input/3
[hadoop@hadoop data]$ hdfs dfs -ls /input
Found 4 items
-rw-r--r-- 3 hadoop supergroup 70 2018-04-21 04:06 /input/1
-rw-r--r-- 3 hadoop supergroup 70 2018-04-21 04:06 /input/2
-rw-r--r-- 3 hadoop supergroup 70 2018-04-21 04:06 /input/3
-rw-r--r-- 3 hadoop supergroup 70 2018-04-20 19:27 /input/input.txt
- 提交任务
spark-submit \
--class cn.zhangyu.WordCountApp \
--master local[2] \
/home/hadoop/lib/wordcount-1.0-SNAPSHOT.jar \
/input /output1
- 结果
查看结果没毛病都乘了4
(hive,4)
(spark,4)
(hadoop,4)
(hdfs,4)
(hello,24)
(java,4)
(sqoop,4)
这时候大家再看日志,有没有发现这样一句话:
18/04/21 04:07:37 INFO FileInputFormat: Total input paths to process : 4
熟悉吗,应该知道什么意思了把,也就是split数量,为什么这样说呢,因为我们测试用的文件很小(小于128MB)如果文件大于128,这里个数就需要和128进行比较了。
6 文件匹配规则
spark-submit \
--class cn.zhangyu.WordCountApp \
--master local[2] \
/home/hadoop/lib/wordcount-1.0-SNAPSHOT.jar \
/input/*.txt /output2
这里输入文件只会带有.txt结尾的。
- 结果
(hive,1)
(hello,6)
(java,1)
(sqoop,1)
(spark,1)
(hadoop,1)
(hdfs,1)
7 wordcount进行排序
import org.apache.spark.{SparkConf, SparkContext}
/**
* Created by grace on 2018/4/27.
*/
object WordCountApp {
def main(args: Array[String]): Unit = {
//创建一个SparkConf
val conf=new SparkConf();
//sparkContext中传递一个SparkConf
val sc=new SparkContext(conf);
//接受一个文件
val textFile=sc.textFile(args(0));
//词频统计
// (这里注意了,自己的文件是按照什么切分的,作者就因为文件中的单词按照空格切分,代码中按照“\t”切分了,一直得不到想要的结果)
val wc=textFile.flatMap(line =>line.split(" ")).map(x=>(x,1)).reduceByKey(_+_);
//根据value进行排序
// 1 map(x=>(x._2,x._1)) 这里相当于把key和value位置互换
// 2 sortByKey(false) 因为sortByKey()默认是按照升序排列的,所以传递一个false
/*
源码:sortByKey默认是ascending=true
def sortByKey(ascending : scala.Boolean = { /* compiled code */ }, numPartitions : scala.Int = { /* compiled code */ })
*/
// 3 .map(x=>(x._2,x._1)) 在进行反转一次,得到我们想要的结果
val sort=wc.map(x=>(x._2,x._1)).sortByKey(false).map(x=>(x._2,x._1))
//控制台输出结果
wc.collect().foreach(println)
//把结果存入到hdfs上
//saveAsTextFile这个方法可以传递codec选择压缩方式进行存储
wc.saveAsTextFile(args(1))
//关闭
sc.stop();
}
}
提示: 这样每次改变代码都要打包上传,比较麻烦,可以使用spark-shell进行测试。