SparkSession与SparkContext关系详解

SparkSession与SparkContext关系详解_第1张图片
SparkSession是Spark 2.0引如的新概念。SparkSession为用户提供了统一的切入点,来让用户学习spark的各项功能。
 在spark的早期版本中,SparkContext是spark的主要切入点,由于RDD是主要的API,我们通过sparkcontext来创建和操作RDD。对于每个其他的API,我们需要使用不同的context。例如,对于Streming,我们需要使用StreamingContext;对于sql,使用sqlContext;对于Hive,使用hiveContext。但是随着DataSet和DataFrame的API逐渐成为标准的API,就需要为他们建立接入点。所以在spark2.0中,引入SparkSession作为DataSet和DataFrame API的切入点,SparkSession封装了SparkConf、SparkContext和SQLContext。为了向后兼容,SQLContext和HiveContext也被保存下来。
 SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。SparkSession内部封装了sparkContext,所以计算实际上是由sparkContext完成的。

//Spark app 配置:应用的名字和Master运行的位置
    val sparkConf=new SparkConf()
      .setAppName("SparkAppTemplate")
      .setMaster("local[2]")
    //创建sparkContext对象:主要用于读取需要处理的数据,封装在RDD集合中;调度jobs执行
    val sc = new SparkContext(sparkConf) 

创建一个SparkSession对象
//在spark 2.x中不推荐使用sparkContext对象读取数据,而是推荐SparkSession
    val spark = SparkSession.builder
      .appName("Simple Application")
      .master("local[2]")
      .getOrCreate()

1.1 启动spark-shell
每个Spark应用程序都需要一个Spark环境,这是Spark RDD API的主要入口点。Spark Shell提供了一个名为“sc”的预配置Spark环境和一个名为“spark”的预配置Spark会话。

使用spark-shell启动交互式命令的过程如下:
进入spark的安装目录,转到bin目录下,启动命令./spark-shell,出现如下图所示的给出上spark和scala的版本号即启动成功。

使用Spark Shell的时候,本身是预配置了sc,即SparkConf和SparkContext的,但是在实际使用编辑器编程过程中是需要设置这些配置的。

1.2 在spark-shell中出现error: not found: value sc问题
在spark-shell中运行val myRDD = sc.parallelize(List(“hdfs”, “spark”, “rdd”))命令时,出现“error: not found: value sc”找不到sc的问题。

原因:在linux系统下,启动交互式命令行时使用的是scala命令,而不是spark-shell命令,这样就找不到spark配置的环境变量,由于每个Spark应用程序都需要一个Spark环境,这样在使用scala命令时就找不到“sc”的预配置,所以会出现上面的问题。

解决方式:进入spark的安装目录,转到spark目录下的bin目录,运行命令./spark-shell命令(而不是scala命令),再运行上面的语句val myRDD = sc.parallelize(List(“hdfs”, “spark”, “rdd”))就不会出现该问题了。

1.3 error: not found: type SparkConf或type SparkContext问题
运行下面的程序时,出现error: not found: type SparkConf或type SparkContext问题:

val conf = new SparkConf().setMaster("master").setAppName("appName1")
val sc = new SparkContext(conf)  //或者val sc = new SparkContext("master","appName1")

解决方式:在该配置语句之前导入以下语句:

import org.apache.spark.SparkConf

同样的,在设置完SparkConf之后,需要设置SparkContext,这时需要导入:
import org.apache.spark.SparkContext,才不至于出现error: not found: type SparkContext的错误。
在spark-shell中的运行过程如下:

scala> import org.apache.spark.SparkConf
import org.apache.spark.SparkConf

scala> val conf = new SparkConf().setMaster("master").set
set      setAppName       setIfMissing   setMaster      
setAll   setExecutorEnv   setJars        setSparkHome   

scala> val conf = new SparkConf().setMaster("master").setAppName("appName")
conf: org.apache.spark.SparkConf = org.apache.spark.SparkConf@c7d173f

scala> val sc = new SparkContext(conf)
:26: error: not found: type SparkContext
       val sc = new SparkContext(conf)
                    ^

scala> import org.apache.spark.Spark
SparkContext                    SparkExecutorInfoImpl   SparkStageInfo       
SparkDriverExecutionException   SparkFiles              SparkStageInfoImpl   
SparkEnv                        SparkFirehoseListener   SparkStatusTracker   
SparkException                  SparkJobInfo                                 
SparkExecutorInfo               SparkJobInfoImpl                             

scala> import org.apache.spark.SparkContext
import org.apache.spark.SparkContext

注意:一旦设置完成SparkConf,就不可被修改

对于单元测试,也可以调用SparkConf(false)来跳过加载外部设置,并获得相同的配置,无论系统属性如何。

1.4 setMaster()和setAppName()源码分析


根据setMaster()setAppName()方法的代码及其注释,可以看出,setMaster主要是连接主节点,如果参数是”local”,则在本地用单线程运行spark,如果是 local[4],则在本地用4核运行,如果设置为spark://master:7077,就是作为单节点运行,而setAppName就是在web端显示应用名而已,它们说到底都调用了set()函数,其定义如下:


上面代码中的logDeprecation(key)是日志输出函数,防止输入参数名无效, 看看settings,是个HashMap结构,追溯一下:

private val settings = new ConcurrentHashMapString, String

1.5 ConcurrentHashMap
settings是个ConcurrentHashMap对象,ConcurrentHashMap主要作用是解决多线程并发下数据段访问效率,该类相对于hashMap而言具有同步map中的数据,对于hashTable而言,该同步数据对于并发程序在效率上有很大的提高,所以在使用缓存机制的时候如果对map中的值具有高并发的情况的话,那么我们就需要使用ConcurrentHashMap,ConcurrentHashMap中主要实体类有三个:ConcurrentHashMap(整个Hash表),Segment(桶),HashEntry(节点)
,CurrentHashMap的初始化一共有三个参数,一个initialCapacity,表示初始的容量,一个loadFactor,表示负载参数,最后一个是concurrentLevel,代表ConcurrentHashMap内部的Segment的数量,ConcurrentLevel一经指定,不可改变,这也是为什么SparkConf配置好了就无法更改的原因。

ConcurrentHashMap应用了锁分段技术,HashTable容器在竞争激烈的并发环境下表现出效率低下,是因为所有访问HashTable的线程都必须竞争同一把锁,那假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

另外,如果ConcurrentHashMap的元素数量增加导致ConrruentHashMap需要扩容,ConcurrentHashMap是不会增加Segment的数量的,而只会增加Segment中链表数组的容量大小,这样的好处是扩容过程不需要对整个ConcurrentHashMap做rehash,而只需要对Segment里面的元素做一次rehash就可以了。

2 SparkSession
SparkSession: SparkSession实质上是SQLContext和HiveContext的组合(未来可能还会加上StreamingContext),所以在SQLContext和HiveContext上可用的API在SparkSession上同样是可以使用的。

2.1 SparkSession的简单示例
SparkSession内部封装了SparkContext,所以计算实际上是由SparkContext完成的。

val sparkSession = SparkSession.builder
.master(“master”)
.appName(“appName”)
.getOrCreate()
//或者SparkSession.builder.config(conf=SparkConf())

上面代码类似于创建一个SparkContext,master设置为”master”,然后创建了一个SQLContext封装它。

2.2 创建支持Hive的SparkSession
如果你想创建hiveContext,可以使用下面的方法来创建SparkSession,以使得它支持Hive(HiveContext):

val sparkSession = SparkSession.builder
.master(“master”)
.appName(“appName”)
.enableHiveSupport()
.getOrCreate()
//sparkSession 从csv读取数据:
val dq = sparkSession.read.option(“header”, “true”).csv(“src/main/resources/scala.csv”)

上面代码中的getOrCreate()方法表示有就拿过来,没有就创建,类似于单例模式:

val s1 = SparkSession().builder.config("k1", "v1").getOrCreat()
val s2 = SparkSession().builder.config("k2", "v2").getOrCreat()
return s1.conf.get("k1") == s2.conf.get("k2")

3 Spark应用框架

客户Spark程序(Driver Program)操作Spark集群是通过SparkContext对象来进行,SparkContext作为一个操作和调度的总入口,在初始化过程中集群管理器会创建DAGScheduler作业调度和TaskScheduler任务调度。
DAGScheduler作业调度模块是基于Stage的高层调度模块,DAG全称 Directed Acyclic Graph,有向无环图。简单的来说,就是一个由顶点和有方向性的边构成的图中,从任意一个顶点出发,没有任何一条路径会将其带回到出发的顶点。它为每个Spark Job计算具有依赖关系的多个Stage任务阶段(通常根据Shuffle来划分Stage,如groupByKey, reduceByKey等涉及到shuffle的transformation就会产生新的stage),然后将每个Stage划分为具体的一组任务,以TaskSets的形式提交给底层的任务调度模块来具体执行。TaskScheduler任务调度模块负责启动任务,监控和汇报任务运行情况。

创建SparkContext一般要经过下面几个步骤:
a). 导入Spark的类和隐式转换
import org.apache.spark.{SparkContext, SparkConf}
import org.apache.spark.SparkContext._

b). 构建Spark应用程序的应用信息对象SparkConf
val conf = new SparkConf().setAppName(appName).setMaster(master_url)

c). 利用SparkConf对象来初始化SparkContext
val sc = new SparkContext(conf)

d). 创建RDD、执行相应的Transformation和Action并得到最终结果。

e). 关闭Context
在完成代码的编写和测试后,使用spark-submit来提交应用的jar包。spark-submit的命令行参考如下:

Submitting Applications

cd /usr/local/spark #进入spark安装目录,这里的/usr/local/spark为spark的安装目录
./bin/spark-submit
–class
–master
–deploy-mode
… # other options

[application-arguments]

Spark的运行模式取决于传递给SparkContext的master环境变量的值。master-url可以是以下任一种形式:
local 使用一个Worker线程本地化运行Spark(完全不并行)
local[*] 使用逻辑CPU数量的线程来本地化运行Spark
local[K] 使用K个Worker线程本地化运行Spark(理想情况下,K应该根据运行机器的CPU核数设定)
spark://HOST:PORT 连接到指定的Spark standalone master。默认端口是7077。
yarn-client 以客户端模式连接YARN集群。集群的位置可以在HADOOP_CONF_DIR 环境变量中找到。
yarn-cluster 以集群模式连接YARN集群。集群的位置可以在HADOOP_CONF_DIR 环境变量中找到。
mesos://HOST:PORT 连接到指定的Mesos集群。默认接口是5050.
而spark-shell会在启动的时候自动构建SparkContext,名称为sc。

你可能感兴趣的:(spark)