在正式安装Spark之前,先给大家介绍下Spark可以在哪几种模式下运行,主要有以下4种运行模式:
local
: 本地单进程模式,用于本地开发测试Spark代码。standalone
:分布式集群模式,Master-Worker架构,Master负责调度,Worker负责具体Task的执行。on yarn/mesos
:运行在yarn/mesos等资源管理框架之上,yarn/mesos提供资源管理,spark提供计算调度,并可与其他计算框架(如MapReduce/MPI/Storm)共同运行在同一个集群之上。on cloud(EC2)
: 运行在AWS的EC2之上本文主要介绍在mac 下如何快速搭建spark单机版。
mac 下基本安装应用都是试用brew。如果没有可以执行如下命令快速安装:
/usr/bin/ruby -e “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)“
brew cask install homebrew/cask/java
brew install scala
brew install apache-spark
spark-shell
这里我介绍两种使用Spark基础Api的方式,一种是在spark-shell中进行简单的测试,一种是在开发工具IDEA中进行代码的编写来教大家快速学习Spark基础API。
由于spark-shell只支持scala和python两种语言的编写,不支持Java,所以我在spark-shell中通过scala的语法来进行简单测试。
在配置好Spark环境变量之后,我们打开命令行,直接在当前用户目录下输入命令spark-shell
进入scala编写环境(当然前提是你首先使用命令start-all.sh
命令开启了Spark):
在spark-shell中完成单词统计:
val file = sc.textFile("/usr/local/Cellar/apache-spark/2.4.4/README.md")
// 以空格为拆分标志,将文件中的每一行分割为多个单词
val words = file.flatMap(line => line.split(" "))
// 对每一个单词进行计数
val wordNumber = words.map(w => (w, 1))
// 将单词进行分类合并,计算每个单词总的出现次数
val wordCounts = wordNumber.reduceByKey(_+_)
//将所有单词及其出现次数打印出来
wordsCounts.foreach(println)
上述工作的简单表述形式:
sc.textFile("/usr/local/Cellar/apache-spark/2.4.4/README.md")
.flatMap(line => line.split(" "))
.map(w => (w, 1))
.reduceByKey(_+_)
.foreach(println)
代码中通过 file://
前缀或者不加 file://
前缀表示指定读取本地文件。如果你这里传入的路径写的是HDFS上的文件路径,例如hdfs://远程主机名:Hadoop端口号我/文件名
代表你要是读取的是 HDFS 中的文件,你需要先上传文件到 HDFS 中,否则会有org.apache.hadoop.mapred.InvalidInputException: Input path does not exist: hdfs://localhost:9000/user/hadoop/README.md
的错误。这里我们就以读取本地文件进行讲解。
RDDs 支持两种类型的操作:1.actions: 在数据集上运行计算后返回值。2.transformations: 转换, 从现有数据集上创建一个新的数据集。
使用上述命令创建好的RDD对象,下面我们就来通过该对象演示 count() 和 first() 操作:
textFile.count() // RDD 中的 item 数量,对于文本文件,就是总行数
textFile.first() // RDD 中的第一个 item,对于文本文件,就是第一行内容
接着演示 transformation,通过 filter transformation 来返回一个新的 RDD,代码如下:
val linesWithSpark = textFile.filter(line => line.contains("Spark")) // 筛选出包含 Spark 的行
linesWithSpark.count() // 统计行数
上述我们完成了RDD的简单计算,而RDD 的 actions 和 transformations 其实可用在更复杂的计算中,例如通过如下代码可以找到包含单词最多的那一行内容共有几个单词:
textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b)
代码首先将每一行内容 map 为一个整数,这将创建一个新的 RDD,并在这个 RDD 中执行 reduce 操作,找到最大的数。map()、reduce() 中的参数是 Scala 的函数字面量(function literals,也称为闭包 closures),并且可以使用语言特征或 Scala/Java 的库。例如,通过使用 Math.max() 函数(需要导入 Java 的 Math 库),可以使上述代码更容易理解:
import java.lang.Math //先导入Math函数
textFile.map(line => line.split(" ").size).reduce((a, b) => Math.max(a, b))
Hadoop MapReduce 是常见的数据流模式,在 Spark 中同样可以实现(下面这个例子也就是 WordCount):
val wordCounts = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b) // 实现单词统计
wordCounts.collect() // 输出单词统计结果
上述我们通过spark-shell完成单词的统计简单对我们spark的基础Api进行了熟悉,采用的是scala语言,由于我是一个java开发人员,所以接下来就在开发工具IDEA中通过编写Java代码来实现HDFS中某个路径下文件内容中单词的统计功能。
既然要统计HDFS中某个文件中的单词,那么我们首先要将文件上传到HDFS上吧!如何上传?听我慢慢道来。
使用命令:quit
退出scala命令环境,首先在本地电脑的当前用户目录下创建一个文件,我这里创建了一个叫hello的txt文件,里面写上内容hello world hello you hello hello ,内容可以随便打啦
,然后输入hadoop的命令(关于Hadoop的更多命令请自行google):hadoop fs -put ~/hello /
,实现将本机目录下的hello文件推至远程主机的根目录下,然后便可以开始编写我们的java代码了。
使用IDEA创建一个Maven项目(便于管理我们的jar包嘛!),在pom.xml中添加上Spark相应jar包坐标:
4.0.0
cn.codingxiaxw.spark
spark-mvn
war
1.0-SNAPSHOT
spark-mvn Maven Webapp
http://maven.apache.org
junit
junit
3.8.1
test
org.apache.spark
spark-core_2.11
2.0.2
spark-mvn
org.apache.maven.plugins
maven-compiler-plugin
1.7
然后创建一个WordCount.java文件,代码如下:
public class Simple
{
private static final Pattern SPACE = Pattern.compile(" ");
public static void main(String[] args) throws Exception {
// if (args.length < 1) {
// System.err.println("Usage: JavaWordCount ");
// System.exit(1);
// }
//创建一个RDD对象
SparkConf conf=new SparkConf().setAppName("Simple").setMaster("local");
//创建spark上下文对象,是数据的入口
JavaSparkContext spark=new JavaSparkContext(conf);
//获取数据源
JavaRDD lines = spark.textFile("hdfs://localhost:8020/hello");
/**
* 对于从数据源得到的DStream,用户可以在其基础上进行各种操作,
* 对于当前时间窗口内从数据源得到的数据首先进行分割,
* 然后利用Map和ReduceByKey方法进行计算,当然最后还有使用print()方法输出结果;
*/
JavaRDD words = lines.flatMap(new FlatMapFunction() {
@Override
public Iterator call(String s) {
return Arrays.asList(SPACE.split(s)).iterator();
}
});
//使用RDD的map和reduce方法进行计算
JavaPairRDD ones = words.mapToPair(
new PairFunction() {
@Override
public Tuple2 call(String s) {
return new Tuple2<>(s, 1);
}
});
JavaPairRDD counts = ones.reduceByKey(
new Function2() {
@Override
public Integer call(Integer i1, Integer i2) {
return i1 + i2;
}
});
List> output = counts.collect();
for (Tuple2,?> tuple : output) {
//输出计算结果
System.out.println(tuple._1() + ": " + tuple._2());
}
spark.stop();
}
}
各行代码意思见代码旁的注释,上述代码都是从官方文档抄的,但是貌似要注释掉官方文档的:
// if (args.length < 1) {
// System.err.println("Usage: JavaWordCount ");
// System.exit(1);
// }
然后运行程序,成功统计出HDFS上文件内容的单词个数。到此,我们便简单熟悉了Spark的相关API,下篇文章我将介绍通过Spark的Streaming Api实现对流式数据的处理为大家介绍Spark中Streming库的相关Api操作。