这一章主要介绍SPARK中的重要抽象,然后用实例讲解如何使用SPARK进行分布式程序的开发。
概述
从上层来看,每个spark应用程序都是由驱动程序构成。这个驱动程序执行我们应用程序的main方法,并在集群上并行执行多种操作。
弹性分布式数据(RDD)是spark提供的重要抽象。它是分布在集群的节点之上的,可以并行计算的一组数据集合。RDDs可以由hadoop文件系统或者其它任何hadoop支持的文件系统上的文件创建,或者驱动程序中的Scala集合创建,或者由这些做转换操作得到。用户也可以将内存中的RDDs持久化,这样可以在并行处理中高效的恢复。最后,RDDs可以自动从失败的节点上还原。
Spark的另外一个抽象是可以在并行处理中使用的共享变量。缺省情况下,当spark在不同的节点上并行的执行一组任务中的函数时,它将函数中用到的每个变量的拷贝传递给每个任务。有些情况下,变量需要在任务之间共享,或者在任务和驱动程序之间共享。
Spark支持两种类型的共享变量:
1.广播变量:可以将共享变量缓存在所有节点上。
2.累加器:只能被用来增加操作,如计数和总和。
下面用SPARK支持语言(scala,java,python)展示一下SPARK的这些特性.如果你启动了spark交互shell,scala-shell或者python的pyspark,将会很容易跟着一起实验这些特性。
链接SPARK
SPARK1.3.1是基于SCALA2.10的。所以用SCALA写程序的话,需要安装SCALA的兼容版本(如SCALA2.10.X)
写SCALA程序,需要在MAVEN依懒中添加SPARK。SPARK在MAVEN中心库上的坐标如下:(如果不了解MAVEN,可以查看我以前博客中对MAVEN的介绍。)
groupId = org.apache.spark
artifactId = spark-core_2.10
version = 1.3.1
另外,如果你想在程序中访问HDFS集群。需要添加对hadoop-client包的依懒,包的版本取决于你要访问的HDFS的版本。典型的HDFS版本标签如下:
groupId = org.apache.hadoop
artifactId = hadoop-client
version = <your-hdfs-version>
最后,在你的程序文件中导入需要使用的SPARK类和需要的显式转换,如下所示:
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkConf
初始化SPARK
每个SPARK应用程序的第一件事就是创建一个SparkContext对象,它用来告诉SPARK如何访问一个集群。创建SparkContext对象需要包含你应用程序的信息。
每个活跃的JVM上只能运行一个SparkContext,你必须停止SparkContext在你创建一个新的之前。
val conf = new SparkConf().setAppName(appName).setMaster(master)
new SparkContext(conf)
appName参数是你应用程序的名称,将会在集群UI上显示。master是一个Spark, Mesos or YARN cluster URL,或者一个特殊的“local”字符串指明运行在本地模式。
在实践中,你不希望在程序里把master写死。你可以用spark-submit启动你的程序,然后通过命令行参数传递master的值。如果是本地测试或者单元测试,你可以传递"local"给你的SPARK程序。
使用SHELL
在Spark-shell里,一个特定的SparkContext已经为你创建好,变量名为sc.不需要你自己创建SparkContext。你可以通过使用--master参数设置context连接的master,你也可以通过传递逗号分隔的列表给--jars参数来向classpath里添加JARs.你还可以为你的shell会话添加依懒的Spark包,通过向--packages参数添加逗号分隔的maven坐标。如果需要另外的版本库,可以通过--repositories参数传递。比如,启动spark-shell使用四核:
$ ./bin/spark-shell --master local[4]
或者添加code.jar到classpath:
$ ./bin/spark-shell --master local[4] --jars code.jar
通过maven坐标包含一个依赖:
$ ./bin/spark-shell --master local[4] --packages "org.example:example:0.1"
可以执行spark-shell --help来查看更多选项。实际上,spark-shell通过调用spark-submit脚本来完成功能。
弹性分布式数据 RDDs
Spark始终围绕着弹性分布式数据(RDD)这个概念。它是一个容错的,可以并行计算的数据集合。有两种创建RDD的方式,在你的驱动程序中并行化已存在的数据集合,或者从外部存储系统引用一个数据集合。这里的外部存储系统,可以是共享文件系统,HDFS,HBase和任何Hadoop支持作为输入的数据集。
并行集合
通过在驱动程序中一个已经存在的数据集合上调用SparkContext的parallelize方法可以创建一个并行集合。集合中的元素被复制以形成一个分布式的数据集合,这样就可以被并行计算。下面是创建一个并行集合的例子。
val data = Array(1, 2, 3, 4, 5)
val distData = sc.parallelize(data)
一旦创建,这个分布式数据集(distData)就能够被并行操作。比如,我们执行distData.reduce((a,b)=>a+b)把集合中的元素相加。稍后会详细讲解分布式数据集上的操作。
分布式数据集的一个重要参数是数据集划分的分区数。Spark将会在集群上为每个分区执行一个任务。典型的应用是在集群的每个CPU上划分2-4个分区。通常情况下,Spark会根据你的集群自动地设置分区的个数。当然,你也可以手动设置通过传递给parallelize第2个参数(e.g.sc.parallelize(data, 10)
).
注:为了保持向后的兼容性,代码的有些地方使用slices(partitions的同义语).
外部数据集
spark能够从hadoop支持的任何数据源创建RDD。包括本地文件系统,HDFS, Cassandra, HBase, Amazon S3. Spark还支持文本文件,序列文件,和任何其它的Hadoop的输入格式。
文本文件RDD可以由SparkContext的textFile方法创建。这个方法的参数是一个文件的URI(可以是一个本地文件路径,或者hdfs://
,s3n://
)。这个方法将文件解析为以行为单位的集合。下面是一个例子:
scala> val distFile = sc.textFile("data.txt")
distFile: RDD[String] = MappedRDD@1d4cee08
创建了RDD后,我们就可以在上面执行操作。我们计算文件中所有行大小的总和,通过使用map,reduce操作。
distFile.map(s => s.length).reduce((a, b) => a + b)
.
Spark读取文件需要注意以下几点:
1.如果使用本地文件路径,需要确保文件能被所有工作节点以相同的路径访问。拷贝文件到所有的节点或者通过mount网络文件系统
2.Spark的所有基于文件输入的方法,包括textFile方法。支持目录,压缩文件,和通配符。比如你可以调用textFile("/my/directory")
,textFile("/my/directory/*.txt")
, 和textFile("/my/directory/*.gz")
.
3.textFile还有一个可选的第二个参数,用来控制文件的分区数。默认情况下,Spark为每个Block创建一个分区。(HDFS中Block默认为64M)。当然你可以传递一个值来使用更大的分区数。注意:你不能使用小于Blocks的分区数。
除了文件文件,Spark的scala API还支持一些其它数据格式:
1.SparkContext的wholeTextFiles让你可以读取包含多个小文件的目录,每个文件的的信息以(文件名,内容)元组的形式返回。这和textFile的返回不同,textFile以每个文件的一行为一条记录的形式返回。
2.SequenceFiles,使用SparkContext的sequenceFile[K, V]
方法,K,V是文件中Key和Value的类型。这些类型必须是Hadoop的Writable接口的子类。比如IntWritable和Text.
另外,Spark允许你指定一些更常见的Writable的原生类型。比如sequenceFile[Int, String]
将自动的转换为IntWritables和Texts.
3.对于其它的Hadoop输入格式,你可以使用SparkContext的hadoopRDD方法,它可以接受任意的JobConf,Input format class,key class,value class.设置这些的方式和Hadoop job是一样的。你也可以对基于新的MapReduce API(org.apache.hadoop.mapreduce)的输入格式,使用SparkContext.newAPIHadoopRDD方法。
4.RDD.saveAsObjectFile和SparkContext.objectFile支持把RDD保存成序列化的JAVA对象。尽管如Avro格式高效,但它提供了简单的保存RDD的方法。
这一章主要讲述了Spark中的重要抽象RDD及共享变量。然后讲解了如何通过maven链接SPARK,进而通过scala开发SPARK应用程序。接着讲述了如何初始SparkContext和使用spark-shell.最后讲述了如何通过API创建几种不同的RDD。
下一章将更为详细的介绍RDD的操作。
翻译整理自:http://spark.apache.org/docs/latest/programming-guide.html