这一章主要介绍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