大数据基础学习-9.Spark2.1.1(未完成)

一、Spark基础

参考:http://www.cnblogs.com/tgzhu/p/5818374.html

1.Spark简介

Spark是一个快速的,通用的,大数据规模的运算引擎。2009年诞生于加州大学伯克利分校AMPLab,2010年开源,2013年6月成为Apache孵化项目,2014年2月成为Apache顶级项目,用 Scala 进行编写。

Spark是基于MapReduce实现的通用的分布式计算框架,继承了MapReduce的优点,同时还支持将Job运算任务产生的中间结果和最终结果保存在内存中,而不像MR放在磁盘,对于迭代运算效率更高,运算速度快。通常当需要处理的数据量超过了单机尺度(比如我们的计算机有4GB的内存,而我们需要处理100GB以上的数据)这时我们可以选择spark集群进行计算,有时我们可能需要处理的数据量并不大,但是计算很复杂,需要大量的时间,这时我们也可以选择利用spark集群强大的计算资源,并行化地计算。

目前,Spark生态系统已经发展成为一个包含多个子项目的集合,其中包含SparkSQL、Spark Streaming、GraphX、 MLib、SparkR等子项目。Spark是基于内存计算的大数据并行计算框架,除了扩展MapReduce 计算模型,还高效地支持更多的计算模式,包括交互式查询和流处理。Spark 适用于原先需要多种分布式平台的场景,包括批处理、迭代算法、交互式查询、流处理。通过在一个统一的框架下支持这些不同的计算,简单而低耗地把各种处理流程整合在一起,大大减轻了原先需要对各种平台分别管理的负担。 spark这种大一统的软件栈,各个组件关系密切并且可以相互调用, 这种设计有几个好处:1、 软件栈中所有的程序库和高级组件 都可以从下层的改进中获益。 2、运行整个软件栈的代价变小了。不需要运行5到10套独立的软件系统了,一个机构只需要运行一套软件系统即可。系统的部署、维护、测试、支持等大大缩减。3、能够构建出无缝整合不同处理模型的应用。

Spark Core实现了Spark的基本功能,包含任务调度、内存管理、错误恢复、与存储系统交互等模块。Spark Core中还包含了对弹性分布式数据集(resilient distributed dataset,简称 RDD)API定义。

Spark SQL:是Spark用来操作结构化数据的程序包。通过Spark SQL,我们可以使用SQL或者Apache Hive版本的SQL方言(HQL)来查询数据。SparkSQL支持多种数据源,比 如Hive表、Parquet以及JSON等。

Spark Streaming:是Spark提供的对实时数据进行流式计算的组件。提供了用来操作数据流的API,并且与Spark Core中的 RDD API高度对应。

Spark MLlib提供常见的机器学习(ML)功能的程序库。包括分类、回归、聚类、协同过滤等,还提供了模型评估、数据导入等额外的支持功能。

GraphX:控制图、并行图操作和计算的一组算法和工具的集合。GraphX扩展了RDD API,包含控制图、创建子图、访问路径上所有顶点的操作。

本篇博客主要介绍spark的基础概念、sparkcore和RDDs。对于sparksql和sparkstreaming将会直接通过项目进行学习。

2.Spark特点

快 :与Hadoop的MapReduce相比,Spark基于内存的运算要快100倍以上,基于硬盘的运算也要快10倍以上。Spark 实现了高效的DAG执行引擎,可以通过基于内存来高效处理数据流。计算的中间结果是存在于内存中的。

易用:Spark支持Java、Python和Scala的API,还支持超过80种高级算法,使用户可以快速构建不同的应用。而且 Spark 支持交互式的 Python 和 Scala 的shell,可以非常方便地在这些 shell 中使用 Spark 集群来验证解决问题的方法。

通用:Spark提供了统一的解决方案。Spark可以用于批处理、交互式查询(Spark SQL)、实时流处理(Spark Streaming)、机器学习(Spark MLlib)和图计算(GraphX)。这些不同类型的处理都可以在同一个应用中无缝使用。Spark统一的解决方案非常具有吸引力,毕竟任何公司都想用统一的平台去处理遇到的问题,减少开发和维护的人力成本和部署平台的物力成本。

兼容性:Spark可以非常方便地与其他的开源产品进行融合。比如,Spark可以使用Hadoop的YARN和Apache Mesos作为它的资源管理和调度器,并且可以处理所有Hadoop支持的数据,包括HDFS、HBase和Cassandra等。这对于已经部署Hadoop集群的用户特别重要,因为不需要做任何数据迁移就可以使用Spark 的强大处理能力。Spark也可以不依赖于第三方的资源管理和调度器,它实现了Standalone作为其内置的资源管理和调度框架,这样进一步降低了Spark的使用门槛,使得所有人都可以非常容易地部署和使用Spark。

 

二、Spark架构

Cluster Manager:在standalone模式中为Master节点,控制整个集群,监控worker;在YARN模式中为资源管理器RM。

Worker节点:从节点,负责控制计算节点,启动Executor或者Driver。

Driver Program:运行Application的main()函数。

Executor:执行器,是为某个Application运行在worker node上的一个进程。

1.Spark运行流程

1)构建Spark Application的运行环境,启动SparkContext。在SparkContext初始化过程中,Spark分别创建作业调度模块DAGScheduler和任务调度模块TaskScheduler(此例为Standalone模式下,在YARN-Client模式下任务调度模块为YarnClientClusterScheduler,在YARN-Cluster模式下为YarnClusterScheduler)。

2)SparkContext向资源管理器(可以是Standalone,Mesos,Yarn)申请运行Executor资源,并启动Standalone Executor backend,这里的executor主要是进程。

3)Executor向SparkContext申请Task。

4)SparkContext将应用程序分发给Executor。

5)SparkContext根据代码构建成DAG图,将DAG图分解成Stage、将Taskset发送给Task Scheduler,最后由Task Scheduler将Task发送给Executor运行。

6)Task在Executor上运行,运行完释放所有资源。

Spark运行特点:

1)每个Application获取专属的executor进程,该进程在Application期间一直驻留,并以多线程方式运行Task。这种Application隔离机制是有优势的,无论是从调度角度看(每个Driver调度他自己的任务),还是从运行角度看(来自不同Application的Task运行在不同JVM中),当然这样意味着Spark Application不能跨应用程序共享数据,除非将数据写入外部存储系统。

2)Spark与资源管理器无关,只要能够获取executor进程,并能保持相互通信就可以了。

3)提交SparkContext的Client应该靠近Worker节点(运行Executor的节点),最好是在同一个Rack里,因为Spark Application运行过程中SparkContext和Executor之间有大量的信息交换。

4)Task采用了数据本地性和推测执行的优化机制,即task最好分配在数据所在的节点上,同时任务只有在触发action操作时,才会真正运行,这在后面将会讲解。

 

2.Spark常用术语

Application: Appliction都是指用户编写的Spark应用程序,其中包括一个Driver功能的代码和分布在集群中多个节点上运行的Executor代码。

Driver: Spark中的Driver即运行上述Application的main函数并创建SparkContext,创建SparkContext的目的是为了准备Spark应用程序的运行环境,在Spark中有SparkContext负责与ClusterManager通信,进行资源申请、任务的分配和监控等,当Executor部分运行完毕后,Driver同时负责将SparkContext关闭,通常用SparkContext代表Driver。

Executor: 某个Application运行在worker节点上的一个进程,该进程负责运行某些Task,并且负责将数据存到内存或磁盘上,每个Application都有各自独立的一批Executor,在Spark on Yarn模式下,其进程名称为CoarseGrainedExecutorBackend。一个CoarseGrainedExecutorBackend有且仅有一个Executor对象, 负责将Task包装成taskRunner,并从线程池中抽取一个空闲线程运行Task,每一个CoarseGrainedExecutorBackend并行运行的Task数量取决与分配给它的cpu个数。

Cluter Manager:指的是在集群上获取资源的外部服务。目前有三种类型:

    Standalone : spark原生的资源管理,由Master负责资源的分配

    Apache Mesos:与hadoop MR兼容性良好的一种资源调度框架

    Hadoop Yarn: 主要是指Yarn中的ResourceManager

Worker: 集群中任何可以运行Application代码的节点,在Standalone模式中指的是通过slave文件配置的Worker节点,在Spark on Yarn模式下就是NodeManager节点。

Task: 被送到某个Executor上的工作单元,和hadoopMR中的MapTask和ReduceTask概念一样,是运行Application的基本单位,多个Task组成一个Stage,而Task的调度和管理等是由TaskScheduler负责。

Job: 包含多个Task组成的并行计算,往往由Spark Action触发生成,一个Application中往往会产生多个Job。

Stage: 每个Job会被拆分成多组Task,作为TaskSet, 其名称为Stage,Stage的划分和调度是有DAGScheduler来负责的,Stage有非最终的Stage(Shuffle Map Stage)和最终的Stage(Result Stage)两种,Stage的边界就是发生shuffle的地方。

DAGScheduler:根据Job构建基于Stage的DAG(Directed Acyclic Graph有向无环图),并提交Stage给TASkScheduler。 其划分Stage的依据是RDD之间的依赖的关系找出开销最小的调度方法。

TASKSedulter: 将TaskSET提交给worker运行,每个Executor运行什么Task就是在此处分配的。TaskScheduler维护所有TaskSet,当Executor向Driver发生心跳时,TaskScheduler会根据资源剩余情况分配相应的Task。另外TaskScheduler还维护着所有Task的运行标签,重试失败的Task。在不同运行模式中任务调度器具体为:

    Spark on Standalone模式为TaskScheduler

    YARN-Client模式为YarnClientClusterScheduler

    YARN-Cluster模式为YarnClusterScheduler

总结如下图。

Job=多个stage,Stage=多个同种task, Task分为ShuffleMapTask和ResultTask,Dependency分为ShuffleDependency和NarrowDependency。

三、Spark编译和安装

在编译前,首先安装maven和scala【本人安装的版本是maven3.3.9和scala2.11】。maven和scala下载解压后,配置环境变量即可使用。同时要保证java在7+版本以上。注意:如果Scala版本低于2.11,需要特别指定,如下。

./dev/change-scala-version.sh 2.10  #解压spark源码包后,进入解压得到的文件夹,在该目录下执行

从官网上下载spark源码,这里下载的是spark-2.1.1.tgz。【如果不想编译可以直接下载相对应的安装包,spark-2.1.1-bin-hadoop2.6.tgz】解压源码包,进入该文件夹,首先查看pom.xml的参数设置,下面只列出两个参数,目的是用来讲解编译的命令。默认的参数如下。

  <properties>
    <java.version>1.7</java.version> #这里可以看到对应的版本信息
    <maven.version>3.3.9</maven.version>
    <hadoop.version>2.2.0</hadoop.version>
    <protobuf.version>2.5.0</protobuf.version>
    <yarn.version>${hadoop.version}</yarn.version>
  </properties>
    <profile>
      <id>hadoop-2.3</id>
      <properties>
        <hadoop.version>2.3.0</hadoop.version>
        <jets3t.version>0.9.3</jets3t.version>
      </properties>
    </profile>

pom.xml中可以看到spark源码所对应hadoop、yarn等版本的具体信息,但是安装的hadoop版本可能是不一样的(例如本人安装的就是hadoop-2.6.0-cdh5.7.0),所以需在编译命令中指定。在执行编译命令前,需要修改一下pom.xml,加入如下的内容,目的是为了能够下载到编译需要的依赖包。

  <repository>
       <id>cloudera</id>
       <url>http://repository.cloudera.com/artifactory/cloudera-repos/</url>
  </repository>

修改pom.xml后,官网建议先执行下面的命令,为maven编译提供足够多的资源,否则容易出现内存不足的错误。

[root@master spark-2.1.0]# export MAVEN_OPTS="-Xmx2g -XX:ReservedCodeCacheSize=512m" 
[root@master spark-2.1.0]# ./build/mvn -Pyarn -Phadoop2.6 -Phive -Phive-thriftserver -Dhadoop.version=2.6.0-cdh5.7.0 -DskipTests clean package #编译命令,同时使spark支持hive

或者采用如下的脚本命令,这个命令编译之后可以生成一个部署包:spark-$VERSION-bin-$NAME.tgz。

[root@masterspark-2.1.0]# ./dev/make-distribution.sh --name 2.6.0-cdh5.7.0 --tgz -Pyarn -Phadoop2.6 -Phive -Phive-thriftserver -Dhadoop.version=2.6.0-cdh5.7.0

其中:-P表示对profile的内容做修改,-D表示对properties的内容做修改,如果命令尾部接“+X”则可以看到更详细的编译信息。具体参考官方文档:http://spark.apache.org/docs/latest/building-spark.html。编译完之后就会在源码的目录下生成安装包,就可以解压安装。

四、spark几种模式搭建

目前Apache Spark主要支持三种分布式部署方式,分别是standalone、spark on mesos和 spark on YARN【另外还有local模式,用于测试程序用】。其中,第一种类似于MapReduce 1.0所采用的模式,内部实现了容错性和资源管理,后两种则是未来发展的趋势,部分容错性和资源管理交由统一的资源管理系统完成:让Spark运行在一个通用的资源管理系统之上,这样可以与其他计算框架,比如MapReduce,公用一个集群资源,最大的好处是降低运维成本和提高资源利用率(资源按需分配)。三种部署方式,优缺点如下。

参考文档:http://dongxicheng.org/framework-on-yarn/apache-spark-comparing-three-deploying-ways/

standalone模式,即独立模式,自带完整的服务,可单独部署到一个集群中,无需依赖任何其他资源管理系统。从一定程度上说,该模式是其他两种的基础。目前Spark在standalone模式下是没有任何单点故障问题的,这是借助zookeeper实现的,思想类似于Hbase master单点故障解决方案。将Spark standalone与MapReduce比较,会发现它们两个在架构上是完全一致的:

1都是由master/slaves服务组成的,且起初master均存在单点故障,后来均通过zookeeper解决;

2各个节点上的资源被抽象成粗粒度的slot,有多少slot就能同时运行多少task。不同的是,MapReduce将slot分为map slot和reduce slot,它们分别只能供Map Task和Reduce Task使用,而不能共享,这是MapReduce资源利率低效的原因之一,而Spark则更优化一些,它不区分slot类型,只有一种slot,可以供各种类型的Task使用,这种方式可以提高资源利用率,但是不够灵活,不能为不同类型的Task定制slot资源。

 

该模式主要的节点有Client节点、Master节点和Worker节点。其中Driver既可以运行在Master节点上中,也可以运行在本地Client端。当用spark-shell交互式工具提交Spark的Job时,Driver在Master节点上运行;当使用spark-submit工具提交Job或者在Eclips、IDEA等开发平台上使用”new SparkConf.setManager(“spark://master:7077”)”方式运行Spark任务时,Driver是运行在本地Client端上的。具体运行流程为:

1)SparkContext连接到Master,向Master注册并申请资源(CPU Core 和Memory);

2)Master根据SparkContext的资源申请要求和Worker心跳周期内报告的信息决定在哪个Worker上分配资源,然后在该Worker上获取资源,然后启动StandaloneExecutorBackend;

3)StandaloneExecutorBackend向SparkContext注册;

4)SparkContext将Applicaiton代码发送给StandaloneExecutorBackend;并且SparkContext解析Applicaiton代码,构建DAG图,并提交给DAG Scheduler分解成Stage(当碰到Action操作时,就会催生Job;每个Job中含有1个或多个Stage,Stage一般在获取外部数据和shuffle之前产生),然后以Stage(或者称为TaskSet)提交给Task Scheduler,Task Scheduler负责将Task分配到相应的Worker,最后提交给StandaloneExecutorBackend执行;

5)StandaloneExecutorBackend会建立Executor线程池,开始执行Task,并向SparkContext报告,直至Task完成;

6)所有Task完成后,SparkContext向Master注销,释放资源。

Spark On Mesos模式。这是很多公司采用的模式,官方推荐这种模式(当然,原因之一是血缘关系)。正是由于Spark开发之初就考虑到支持Mesos,因此,目前而言,Spark运行在Mesos上会比运行在YARN上更加灵活,更加自然。目前在Spark On Mesos环境中,用户可选择两种调度模式之一运行自己的应用程序:

1)粗粒度模式(Coarse-grained Mode):每个应用程序的运行环境由一个Dirver和若干个Executor组成,其中,每个Executor占用若干资源,内部可运行多个Task(对应多少个“slot”)。应用程序的各个任务正式运行之前,需要将运行环境中的资源全部申请好,且运行过程中要一直占用这些资源,即使不用,最后程序运行结束后,回收这些资源。举个例子,比如你提交应用程序时,指定使用5个executor运行你的应用程序,每个executor占用5GB内存和5个CPU,每个executor内部设置了5个slot,则Mesos需要先为executor分配资源并启动它们,之后开始调度任务。另外,在程序运行过程中,mesos的master和slave并不知道executor内部各个task的运行情况,executor直接将任务状态通过内部的通信机制汇报给Driver,从一定程度上可以认为,每个应用程序利用mesos搭建了一个虚拟集群自己使用。

2)细粒度模式(Fine-grained Mode):鉴于粗粒度模式会造成大量资源浪费,Spark On Mesos还提供了另外一种调度模式:细粒度模式,这种模式类似于现在的云计算,思想是按需分配。与粗粒度模式一样,应用程序启动时,先会启动executor,但每个executor占用资源仅仅是自己运行所需的资源,不需要考虑将来要运行的任务,之后,mesos会为每个executor动态分配资源,每分配一些,便可以运行一个新任务,单个Task运行完之后可以马上释放对应的资源。每个Task会汇报状态给Mesos slave和Mesos Master,便于更加细粒度管理和容错,这种调度模式类似于MapReduce调度模式,每个Task完全独立,优点是便于资源控制和隔离,但缺点也很明显,短作业运行延迟大。

Spark On YARN模式。这是一种最有前景的部署模式。Spark on YARN模式根据Driver在集群中的位置分为两种模式:一种是YARN-Client模式,另一种是YARN-Cluster(或称为YARN-Standalone模式)。

Yarn-Client模式:Driver在客户端本地运行,这种模式可以使得Spark Application和客户端进行交互,因为Driver在客户端,所以可以通过webUI访问Driver的状态,默认是http://hostname:4040访问,而YARN通过http:// hostname:8088访问。YARN-client的工作流程步骤为:

 

1)Spark Yarn Client向YARN的ResourceManager申请启动Application Master。同时在SparkContent初始化中将创建DAGScheduler和TASKScheduler等,由于我们选择的是Yarn-Client模式,程序会选择YarnClientClusterScheduler和YarnClientSchedulerBackend;

2)ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,与YARN-Cluster区别的是在该ApplicationMaster不运行SparkContext,只与SparkContext进行联系进行资源的分配;

3)Client中的SparkContext初始化完毕后,与ApplicationMaster建立通讯,向ResourceManager注册,根据任务信息向ResourceManager申请资源(Container);

4)一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向Client中的SparkContext注册并申请Task;

5)client中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrainedExecutorBackend运行Task并向Driver汇报运行的状态和进度,以让Client随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务;

6)应用程序运行完成后,Client的SparkContext向ResourceManager申请注销并关闭自己。

YARN-Cluster模式:当用户向YARN中提交一个应用程序后,YARN将分两个阶段运行该应用程序:

第一个阶段是把Spark的Driver作为一个ApplicationMaster在YARN集群中先启动;

第二个阶段是由ApplicationMaster创建应用程序,然后为它向ResourceManager申请资源,并启动Executor来运行Task,同时监控它的整个运行过程,直到运行完成。

1)Spark Yarn Client向YARN中提交应用程序,包括ApplicationMaster程序、启动ApplicationMaster的命令、需要在Executor中运行的程序等;

2)ResourceManager收到请求后,在集群中选择一个NodeManager,为该应用程序分配第一个Container,要求它在这个Container中启动应用程序的ApplicationMaster,其中ApplicationMaster进行SparkContext等的初始化;

3)ApplicationMaster向ResourceManager注册,这样用户可以直接通过ResourceManage查看应用程序的运行状态,然后它将采用轮询的方式通过RPC协议为各个任务申请资源,并监控它们的运行状态直到运行结束;

4)一旦ApplicationMaster申请到资源(也就是Container)后,便与对应的NodeManager通信,要求它在获得的Container中启动CoarseGrainedExecutorBackend,CoarseGrainedExecutorBackend启动后会向ApplicationMaster中的SparkContext注册并申请Task。这一点和Standalone模式一样,只不过SparkContext在Spark Application中初始化时,使用CoarseGrainedSchedulerBackend配合YarnClusterScheduler进行任务的调度,其中YarnClusterScheduler只是对TaskSchedulerImpl的一个简单包装,增加了对Executor的等待逻辑等;

5)ApplicationMaster中的SparkContext分配Task给CoarseGrainedExecutorBackend执行,CoarseGrained Executor Backend运行Task并向ApplicationMaster汇报运行的状态和进度,以让ApplicationMaster随时掌握各个任务的运行状态,从而可以在任务失败时重新启动任务;

6)应用程序运行完成后,ApplicationMaster向ResourceManager申请注销并关闭自己。

Spark Client 和 SparkCluster的区别:

理解YARN-Client和YARN-Cluster深层次的区别之前先清楚一个概念:Application Master。在YARN中,每个Application实例都有一个ApplicationMaster进程,它是Application启动的第一个容器。它负责和ResourceManager打交道并请求资源,获取资源之后告诉NodeManager为其启动Container。从深层次的含义讲YARN-Cluster和YARN-Client模式的区别其实就是ApplicationMaster进程的区别。

YARN-Cluster模式下,Driver运行在AM(Application Master)中,它负责向YARN申请资源,并监督作业的运行状况。当用户提交了作业之后,就可以关掉Client,作业会继续在YARN上运行,因而YARN-Cluster模式不适合运行交互类型的作业。

YARN-Client模式下,ApplicationMaster仅仅向YARN请求Executor,Client会和请求的Container通信来调度他们工作,也就是说Client不能离开。

1.local模式

local模式主要用于程序的调试,--master用来指定运行模式,采用本地模式即local,2代表运行程序的线程个数。

[root@masteractive src]# spark-shell --master local[2] #运行后将会打开scala的命令行界面,可访问spark集群

[root@masteractive src]#./bin/run-example SparkPi 10 --master local[2] #运行一个spark例子,计算pi的值

2.Standalone模式

[root@master conf]# cp spark-env.sh.template spark-env.sh

修改内容如下。

SPARK_MASTER_HOST=masteractive #master的节点名称
SPARK_WORKER_CORES=1  #配置每个worker core数量
SPARK_WORKER_MEMORY=1G #配置work内存大小
SPARK_WORKER_INSTANCES=1 #配置节点worker的个数
[root@master conf]# cp slaves.template slaves #在slave中写上从节点的hostname,和hadoop集群类似。

将修改的配置文件发送给子节点,之后启动集群。

[root@master sbin]# ./start-all.sh 

启动后,主节点有个master进程,从节点有个worker进程,通过http://192.168.101.101:8080/查看集群信息。

[root@master spark-2.2.0-bin-2.6.0-cdh5.7.0]# spark-shell --master spark://masteractive:7077 现在通过7077端口对集群进行访问

此外可以配置Job History Server 。

将 spark-default.conf.template 复制为 spark-default.conf,并添加如下内容。

spark.eventLog.enabled  true
spark.eventLog.dir  hdfs://masteractive:9000/sparkeventlog
spark.eventLog.compress true

在spark-env.sh中,添加如下内容。

export SPARK_HISTORY_OPTS="-Dspark.history.ui.port=4000 -Dspark.history.retainedApplications=3 -Dspark.history.fs.logDirectory=hdfs://masteractive:9000/sparkeventlog"

相关参数描述:

spark.eventLog.dir: Application 在运行过程中所有的信息均记录在该属性指定的路径下。

spark.history.ui.port=4000 调整 WEBUI 访问日志的端口号为 4000。

spark.history.fs.logDirectory=hdfs://masteractive:9000/sparkeventlog。配置 了该属性后,在 start-history-server.sh 时就无需再显式的指定路径, Spark History Server 页面只展示该指定路径下的日志信息。

spark.history.retainedApplications=3 指定保存 Application 历史记录的个数,如果超过这个值,旧的应用程序信息将被删除。

现在用standalone集群来运算pi。 

[root@masteractive spark-2.1.1-bin-hadoop2.6]# ./bin/spark-submit --class org.apache.spark.examples.SparkPi --master spark://masteractive:7077 examples/jars/spark-examples_2.11-2.1.1.jar 100 #迭代100次,运算结果更加准确

3.sparkHA

集群部署完了,但Master 节点存在单点故障,要解决此问题,就要借助 zookeeper,并且启动至少两个 Master 节点来实现高可靠,配置方式比较简单。

停止 spark 所有服务,修改配置文件 spark-env.sh,在该配置文件中删掉SPARK_MASTER_IP 并添加如下配置。

export SPARK_DAEMON_JAVA_OPTS="-Dspark.deploy.recoveryMode=ZOOKEEPER -Dspark.deploy.zookeeper.url=masteractive:2181,slave1:2181 -Dspark.deploy.zookeeper.dir=/spark" #将masteractive和slave1设置成master节点。

分别在masteractive和slave1中启动master节点。

 sbin/start-master.sh

程序中 spark 集群的访问地址需要改成:

spark-shell --master spark://masteractive:7077,slave1:7077 

 

4.yarn模式

修改 Hadoop 配置下的 yarn-site.xml:

<configuration>
<!-- Site specific YARN configuration properties -->
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>masteractive</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
<!--是否启动一个线程检查每个任务正使用的物理内存量,如果任务超出分配值,则直接将其杀掉,默认是 true -->
    <property>
        <name>yarn.nodemanager.pmem-check-enabled</name>
        <value>false</value>
    </property>
<!--是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是 true -->
    <property>
        <name>yarn.nodemanager.vmem-check-enabled</name>
        <value>false</value>
    </property>
</configuration>

修改 spark-env.sh 添加:

HADOOP_CONF_DIR=/usr/local/src/hadoop-2.6.0-cdh5.7.0/etc/hadoop
YARN_CONF_DIR=/usr/local/src/hadoop-2.6.0-cdh5.7.0/etc/hadoop

启动yarn任务,计算pi。

./bin/spark-submit --class org.apache.spark.examples.SparkPi --master yarn-cluster examples/jars/spark-examples_2.11-2.1.1.jar 10

五、运行spark程序

在上面配置不同模式的过程中,运行了计算pi的spark程序。下面对这些参数进行具体讲解。

1.提交应用的脚本

 

一旦打包好,就可以使用 bin/spark-submit 脚本启动应用了。这个脚本负责设置 spark 使用的 classpath 和依赖,支持不同类型的集群管理器和发布模式:

./bin/spark-submit \
--class <main-class>
--master <master-url> \
--deploy-mode <deploy-mode> \
--conf <key>=<value> \
... # other options
<application-jar> \
[application-arguments]

一些常用选项:

 ① --class: 你的应用的启动类 (如 org.apache.spark.examples.SparkPi)

 ② --master: 集群的 master URL (如 spark://23.195.26.187:7077)

 ③ --deploy-mode: 是否发布你的驱动到 worker 节点(cluster) 或者作为一个本地客户端 (client) (default:client)

 ④ --conf: 任意的 Spark 配置属性, 格式 key=value。如果值包含空格,可以加引号“key=value”。

 ⑤ application-jar: 打包好的应用 jar,包含依赖。

 ⑥ application-arguments: 传给 main()方法的参数。

Master URL 可以是以下格式: 

local本地以一个 worker 线程运行(例如非并行的情况).
local[K]本地以 K worker 线程 (理想情况下, K 设置为你机器
的 CPU 核数).
local[*]本地以本机同样核数的线程运行.
spark://HOST:PORT连接到指定的 Spark standalone cluster master. 端口是
你的 master 集群配置的端口,缺省值为 7077.
mesos://HOST:PORT连接到指定的 Mesos 集群. Port 是你配置的 mesos 端
口, 缺省是 5050. 或者如果 Mesos 使用 ZOoKeeper,
格式为 mesos://zk://....
yarn-client以 client 模式连接到 YARN cluster. 集群的位置基于
HADOOP_CONF_DIR 变量找到.
yarn-cluster以 cluster 模式连接到 YARN cluster. 集群的位置基于
HADOOP_CONF_DIR 变量找到.

2.执行spark 程序(standalone)

spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://masteractive:7077 \
--executor-memory 1G \
--total-executor-cores 2 \
/usr/local/src/spark-2.1.1-bin-hadoop2.6/examples/jars/spark-examples_2.11-2.1.1.jar 

--master spark://masteractive:7077    指定 Master 的地址

--executor-memory 1G                      指定每个 executor 可用内存为 1G

--total-executor-cores 2                     指定每个 executor 使用的 cup 核数为 2 个

该算法是利用蒙特·卡罗算法求 PI,查看日志页面如下。

3.执行spark 程序(yarn)

spark-submit \
--class org.apache.spark.examples.SparkPi \
--master spark://masteractive:7077 \
--deploy-mode client \
/usr/local/src/spark-2.1.1-bin-hadoop2.6/examples/jars/spark-examples_2.11-2.1.1.jar #这个也可是自己编写的代码jar包

4.Wordcount案例(standalone)

先用standalone的模式启动spark-shell。(参见上文)

scala> var file = spark.sparkContext.textFile("/usr/local/src/data/1.txt") #读取文件
scala> val wordCounts = file.flatMap(line =>line.split(" ")).map((word =>(word,1))).reduceByKey(_+_) #计算逻辑
scala> wordCounts.collect #计算,并输出结果
res0: Array[(String, Int)] = Array((dajifd,1), (sdajif,1), (aijdjaiZZ,1), (sajdfi,1), (i,1), (ba,1))

上面讲解了spark的编译安装、几种模式和提交程序的方法,如果能正常出结果,那么说明这个spark的集群可以使用了。下面具体开始介绍spark内部的实现机制,了解spark是如何工作的。

六、Sparkcore

1.RDD概念

RDD 是 Spark 的基石,是实现 Spark 数据处理的核心抽象。Hadoop 的 MapReduce 是一种基于数据集的工作模式,面向数据,这种工作模式一般是从存储上加载数据集,然后操作数据集,最后写入物理存储设备。数据更多面临的是一次性处理。MR 的这种方式对数据领域两种常见的操作不是很高效。第一种是迭代式的算法。比如机器学习中 ALS、凸优化梯度下降等。这些都需要基于数据集或者数据集的衍生数据反复查询反复操作。 MR 这种模式不太合适,即使多 MR串行处理,性能和时间也是一个问题。数据的共享依赖于磁盘。另外一种是交互式数据挖掘, MR 显然不擅长。

我们需要一个效率非常快, 且能够支持迭代计算和有效数据共享的模型,Spark 应运而生。 RDD 是基于数据集的工作模式, 更多的是面向工作流。

RDD(Resilient Distributed Dataset)叫做弹性分布式数据集,是 Spark 中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合,可以简单的理解为在磁盘上有个数据集,这个数据集加载到spark程序中就变成了RDD的概念,接下来所说的RDD,就可理解为加载到内存中的数据集。在Spark中,对数据的所有操作不外乎创建RDD、转化已有RDD以及调用RDD操作进行求值。 每个RDD都可被分为多个分区,这些分区运行在集群中的不同节点上。RDD可以包含Python、Java、Scala中任意类型的对象, 甚至可以包含用户自定义的对象。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将数据集缓存在内存中,后续的查询能够重用数据集,这极大地提升了查询速度。

RDD支持两种操作:转化操作(transformation)和行动操作(action)。RDD的转化操作是返回一个新的RDD的操作,比如 map()和 filter(),而行动操作则是向驱动器程序返回结果或把结果写入外部系统的操作, 比如count()和 first()。Spark 采用惰性计算模式,RDD只有第一次在一个行动操作中用到时,才会真正计算。Spark可以优化整个计算过程。 默认情况下,Spark的 RDD会在你每次对它们进行行动操作时重新计算。如果想在多个行动操作中重用同一个RDD,可以使用 RDD.persist() 让 Spark 把这个 RDD 缓存下来。

2.RDD 属性

1)一组分片(Partition),即数据集的基本组成单位。对于 RDD 来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。用户可以在创建RDD时指定RDD的分片个数,如果没有指定,那么就会采用默认值。默认值就是程序所分配到的CPU Core的数目。

2一个计算每个分区的函数。SparkRDD的计算是以分片为单位的,每个RDD都会实现compute函数以达到这个目的。 compute函数会对迭代器进行复合,不需要保存每次计算的结果。

3RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于流水线一样的前后依赖关系。在部分分区数据丢失时,Spark 可以通过这个依赖关系重新计算丢失的分区数据,而不是对 RDD 的所有分区进行重新计算。

4 一个Partitioner,即RDD的分片函数。当前 Spark 中实现了两种类型的分片函数 ,一 个是基于哈希的HashPartitioner , 另外一个是基于范围的RangePartitioner。只有对于有key-valueRDD,才会有Partitioner,非keyvalueRDDParititioner 的值是NonePartitioner函数不但决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。

5一个列表,存储存取每个Partition的优先位置(preferred location)。对于一 个 HDFS 文件来说,这个列表保存的就是每个 Partition所在的块的位置。按照“移动数据不如移动计算”的理念, Spark 在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置。

RDD是一个应用层面的逻辑概念。 一个RDD多个分片。 RDD就是一个元数据记录集,记录了 RDD 内存所有的关系数据。

3.RDD特点

 

RDD 表示只读的分区的数据集,对 RDD 进行改动,只能通过 RDD 的转换操作,由一个 RDD 得到一个新的 RDD,新的 RDD 包含了从其他 RDD 衍生所必需的信息。 RDDs 之间存在依赖, RDD 的执行是按照血缘关系延时计算的。如果血缘关系较长,可以通过持久化 RDD 来切断血缘关系。

 

3.1分区

RDD 逻辑上是分区的,每个分区的数据是抽象存在的,计算的时候会通过一个 compute 函数得到每个分区的数据。如果 RDD 是通过已有的文件系统构建,则 compute 函数是读取指定文件系统中的数据,如果 RDD 是通过其他RDD 转换而来,则 compute 函数是执行转换逻辑将其他 RDD 的数据进行转换。

RDD 是只读的,要想改变 RDD 中的数据,只能在现有的RDD 基础上创建新的 RDD。由一个 RDD 转换到另一个 RDD,可以通过丰富的操作算子实现 。

 

 

3.2依赖

RDDs 通过操作算子进行转换,转换得到的新 RDD 包含了从其他 RDDs衍生所必需的信息, RDDs 之间维护着这种血缘关系,也称之为依赖。如下图所示,依赖包括两种,一种是窄依赖, RDDs 之间分区是一一对应的,另一种是宽依赖,下游 RDD 的每个分区与上游 RDD(也称之为父 RDD)的每个分区都有关,是多对多的关系。 

通过 RDDs 之间的这种依赖关系,一个任务流可以描述为 DAG(有向无环图),如下图所示,在实际执行过程中宽依赖对应于 Shuffle(图中的reduceByKey join),窄依赖中的所有转换操作可以通过类似于管道的方式一气呵成执行(图中 map union 可以一起执行)

 

3.3缓存

 

如果在应用程序中多次使用同一个 RDD,可以将该 RDD 缓存起来,该RDD 只有在第一次计算的时候会根据血缘关系得到分区的数据,在后续其他地方用到该 RDD 的时候,会直接从缓存处取而不用再根据血缘关系计算,这样就加速后期的重用。如下图所示, RDD-1 经过一系列的转换后得到 RDD-n并保存到 hdfs, RDD-1 在这一过程中会有个中间结果,如果将其缓存到内存,那么在随后的 RDD-1 转换到 RDD-m 这一过程中,就不会计算其之前的RDD-0 了。 

3.4checkpoint

虽然 RDD 的血缘关系天然地可以实现容错,当 RDD 的某个分区数据失败或丢失,可以通过血缘关系重建。但是对于长时间迭代型应用来说,随着迭代的进行, RDDs 之间的血缘关系会越来越长,一旦在后续迭代过程中出错,则需要通过非常长的血缘关系去重建,势必影响性能。为此, RDD 支持checkpoint 将数据保存到持久化的存储中,这样就可以切断之前的血缘关系,因为 checkpoint 后的 RDD 不需要知道它的父 RDDs 了,它可以从 checkpoint处拿到数据。

给定一个 RDD 我们至少可以知道如下几点信息: 1、分区数以及分区方式; 2、由父 RDDs 衍生而来的相关依赖信息; 3、计算每个分区的数据,计算步骤为: 1)如果被缓存,则从缓存中取的分区的数据; 2)如果被checkpoint,则从 checkpoint 处恢复数据; 3)根据血缘关系计算分区的数据。 

4.RDD编程

这里主要介绍常用函数,具体使用自行找资料学习。

 

map(func)返回一个新的 RDD,该 RDD 由每一个输入元素经过 func 函数转换后组成
filter(func)返回一个新的 RDD,该 RDD 由经过 func函数计算后返回值为 true 的输入元素组成
flatMap(func)类似于 map,但是每一个输入元素可以被映射为 0 或多个输出元素(所以 func 应该返回一个序列,而不是单一元素)
mapPartitions(func)类似于 map,但独立地在 RDD 的每一个分片上运行,因此在类型为 T 的 RDD 上运行时, func 的函数类型必须是Iterator[T] => Iterator[U]。 假设有 N 个元素,有 M 个分区,那么 map 的函数的将被调用 N 次,而 mapPartitions 被调用 M 次,一个函数一次处理所有分区
mapPartitionsWithIndex(func)类似于 mapPartitions,但 func 带有一个整数参数表示分片的索引值,因此在类型为T 的 RDD 上运行时, func 的函数类型必须是(Int, Interator[T])  => Iterator[U]
sample(withReplacement,fraction, seed)以指定的随机种子随机抽样出数量为fraction 的数据,ithReplacement 表示是抽出的数据是否放回, true 为有放回的抽样, false 为无放回的抽样, seed 用于指定随机数生成器种子。例子从 RDD 中随机且有放回的抽出 50%的数据,随机种子值为 3(即可能以 1 2 3 的其中一个起始值)
union(otherDataset)对源 RDD 和参数 RDD 求并集后返回一个新的 RDD
intersection(otherDataset)对源 RDD 和参数 RDD 求交集后返回一个新的 RDD
distinct([numTasks]))对源 RDD 进行去重后返回一个新的 RDD.默认情况下,只有 8 个并行任务来操作,但是可以传入一个可选的 numTasks 参数改变它。
partitionBy对 RDD 进行分区操作,如果原有的partionRDD 和现有的 partionRDD 是一致的话就不进行分区,否则会生成 ShuffleRDD.
reduceByKey(func,[numTasks])在一个(K,V)的 RDD 上调用,返回一个(K,V)的 RDD,使用指定的 reduce 函数,将相同 key 的值聚合到一起, reduce 任务的个数可以通过第二个可选的参数来设置
groupByKeygroupByKey 也是对每个 key 进行操作,但只生成一个 sequence。
combineByKey[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C)
对相同 K,把 V 合并成一个集合.
createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就 和之前的某个元素的键相同。
如果这是一个新的元素,combineByKey() 会使用一个叫作createCombiner() 的函数来创建那个键对应的累加器的初始值
mergeValue: 如果这是一个在处理当前分区之前已经遇到的键, 它会使用 mergeValue() 方法将该键的累加器对应的当前值与这个新的值进
行合并
mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的
累加器, 就需要使用用户提供的
mergeCombiners() 方法将各个分区的结果进行合并。
aggregateByKey(zeroValue:U,[partitioner: Partitioner]) (seqOp:
(U, V) => U,combOp: (U, U) =>U)
在 kv 对的 RDD 中,,按 key 将 value 进行分组合并,合并时,将每个 value 和初始值作为 seq 函数的参数,进行计算,返回的结果作为一个新的 kv 对,然后再将结果按照 key 进行合并,最后将每个分组
的 value 传递给 combine 函数进行计算(先将前两个 value 进行计算,将返回结果和下一个 value 传给 combine 函数,以此类推),将 key 与计算结果作为一个新的 kv 对输出。seqOp 函数用于在每一个分区中用初始值逐步迭代 value, combOp 函数用于合并每个分区中的结果














 
 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值