SparkConf
SparkConf 是Spark的配置类,Spark中的每一个组件都直接或者间接的使用这个类存储的属性.SparkConf中,使用ConcurrentHaskMap来存储这些属性,其中key以及value都是String
类型的.
/** 线程安全的,用于存储配置的各种属性 */
private val settings = new ConcurrentHashMap[String, String]()
SparkConf的构造器中有一个布尔类型的loadDefaults,当loadDefaults为true时,将会从系统属性中加载Spark配置,而这些配置的key都是以spark.
开头的属性:
if (loadDefaults) {
// 加载系统中以spark.开头的系统属性
loadFromSystemProperties(false)
}
/**
* 加载系统中以spark.开始的系统属性
*
* @param silent 是否检查过时属性并打印警告️信息 true:不检查 false 检查
* @return
*/
private[spark] def loadFromSystemProperties(silent: Boolean): SparkConf = {
// Load any spark.* system properties
for ((key, value) <- Utils.getSystemProperties if key.startsWith("spark.")) {
set(key, value, silent)
}
this
}
我们可以从loadFromSystemProperties方法中可以看到,使用Utils工具类获取到系统属性后,进行遍历,遍历时如果是以spark.
开头的,就调用SparkConf的set方法存储到setting属性中.set方法的源码如下:
private[spark] def set(key: String, value: String, silent: Boolean): SparkConf = {
// 检查key和value,保证key和value都不为null
if (key == null) {
throw new NullPointerException("null key")
}
if (value == null) {
throw new NullPointerException("null value for " + key)
}
// 是否检查过时警告:false 检查
if (!silent) {
logDeprecationWarning(key)
}
// 如果key和value都不为null,将key和value存储到settings中
settings.put(key, value)
// 返回当前SparkConf的实例
this
}
从SparkConf的源码中可以看到,set方法被重载了多个,但是,最终都下面这一个set方法:
/** Set a configuration variable. */
def set(key: String, value: String): SparkConf = {
set(key, value, false)
}
常用的通过SparkConf设置Master,AppName等属性:
/**
* The master URL to connect to, such as "local" to run locally with one thread, "local[4]" to
* run locally with 4 cores, or "spark://master:7077" to run on a Spark standalone cluster.
*/
def setMaster(master: String): SparkConf = {
set("spark.master", master)
}
/** Set a name for your application. Shown in the Spark web UI. */
def setAppName(name: String): SparkConf = {
set("spark.app.name", name)
}
/** Set JAR files to distribute to the cluster. */
def setJars(jars: Seq[String]): SparkConf = {
for (jar <- jars if (jar == null)) logWarning("null jar passed to SparkContext constructor")
set("spark.jars", jars.filter(_ != null).mkString(","))
}
Spark提供了设置属性,当然也提供了诸多获取属性的方法,但是最终调用的也都是下面这一个:
/** Get a parameter as an Option */
def getOption(key: String): Option[String] = {
Option(settings.get(key)).orElse(getDeprecatedConfig(key, this))
}
在SparkConf的伴生对象中,还将对应版本号过时的配置信息存储到deprecatedConfigs
中,对饮版本可选参数存储到configsWithAlternatives中,更多关于配置信息,请参见源码.
在有些情况下,同一个SparkConf实例中的配置信息需要被Spark中的多个组件共用,在SparkConf的源码中可以看到,其继承了Cloneable特质并实现了clone方法,功能与java的一样,就是可以通过克隆来创建.
Spark内置的RPC框架
在Spark中很多地方都涉及到网络通信,比如Spark各个组件间的消息互通,用户文件与Jar包上传,节点间的Shuffle过程,Block数据的复制与备份等.
在Spark2.0,0版本中节点间Shuffle过程和Block数据的复制与备份依然使用Netty.通过对接口和程序接口的重新设计,将各个组件间的消息互通,用户文件与Jar包上传等内容统一纳入Spark的RPC框架体系中.
Spark内置RPC框架的基本架构:
TransportContext内部包含传输上下文的配置信息TransportConf和对客户端请求消息进行处理的RpcHandler.TransportCOnf在创建TransportClientFactory和TransportServer时都是必须的,而RpcHandler只用于创建TransportServer.TransportClientFactory是RPC客户端的工厂类.TransportServer是RPC服务端的实现.图中记号的含义如下:
①表示通过调用TransportContext的createClientFactory方法创建传输客户端工厂TransportClientFactory的实例.在构造TransportClientFactory的时候,还会传递客户端引导程序TransportClientBootstrap的列表,此外,TransportClientFactory内部还存在针对每一个Socket地址的连接池ClientPool,这个连接池的定义如下:
private final ConcurrentHashMap<SocketAddress, ClientPool> connectionPool;
ClientPool定义如下:
private static class ClientPool {
// ClientPool由TransportClient构成
TransportClient[] clients;
// 与每一个TransportClient 一一对应的锁对象,
// 通过对每个TransportClient分别采用不同的锁,降低并发情况下线程间对锁的争用,减少阻塞,提高并发度
Object[] locks;
ClientPool(int size) {
clients = new TransportClient[size];
locks = new Object[size];
for (int i = 0; i < size; i++) {
locks[i] = new Object();
}
}
}
②标示通过调用TransportContext的createServer方法创建传输服务端TransportServer的实例.在狗仔TransportServer的实例时,需要传递TransportContext,host,port,RpcHandler以及服务端引导程序TransportServerBootstrap的列表.
Spark RPC框架所包含的各个组件.
-
TransportContext:传输上下文,包含了用于创建传输服务端(Transportserver)和传输客户端工厂(TransportClientFactory)的上下文信息,并支持使用TransportChannelHandler设置Netty提供的SocketChannel的Pipeline的实现.
-
TransportConf:传输上下文的配置信息
-
RpcHandler:对调用传输客户端(TransportClient)的sendRPC方法发送的消息进行处理的程序.
-
MessageEncoder: 在将消息放入管道前,先对消息内容进行编码,防止管道另一端读取时丢包和解析错误.
-
MessageDecoder:对从管道中读取的ByteBuf进行解析,防止丢包和解析错误.
-
TransportFreameDecoder:对从管道中读取的ByteBuf按照数据帧进行解析.
-
RpcResponseCallBack:RpcHandler对请求的消息处理完毕后进行回掉的接口.
-
TransportClientFactory:创建TransportClient的传输客户端工厂类.
-
ClientPool :在两个对等节点间维护的关于TransportClient的池子.ClientPool时TransportClientFacctory的内部组件.
-
TransportClient:RPC框架的客户端,用于获取预先协商好的流中的连续块.TransportClient旨在允许有效传输大量的数据,这些数据将被拆分成几百KB到MB的块.TransportClient处理从流中获取的块时,实际的设置是在传输层之外完成的.sendRPC方法能够在客户端和服务端的统一水平线进行这些设置.
-
TransportClintBootstrap:当服务端响应客户端连接时在客户端执行一次的引导程序.
-
TransportRequestHandler:用于处理客户端的请求并写完块数据后返回的处理程序.
-
TransportChannelHandler:代理由TransportRequestHandler处理的请求和由TransportResponseHandler处理的响应,并传入传输层的处理.
-
TransportServerBootstrap:当客户端连接到服务端时在服务端执行一次的引导程序.
-
TransportServer:RPC框架的服务端,提供高效,低级别的流服务
TransportConf
TransportConf:传输上下文的配置信息
可以通过SparkTransportConf的fromSparkConf
方法获取TransportConf实例,获取时需要SparkConf实例,module名称,用于处理网络传输的内核数numUsableCores
SparkTransportConf源码:
object SparkTransportConf {
/**
* 使用Netty线程数的上限
* 实际上,只需要2-4核就可以传输速度就可以达到10Gb/s,每个内核的使用初始开销大约为32M堆外内存.
*/
private val MAX_DEFAULT_NETTY_THREADS = 8
/**
* Utility for creating a [[TransportConf]] from a [[SparkConf]].
*
* @param _conf the [[SparkConf]]
* @param module the module name
* @param numUsableCores 如果非0,则将服务器和客户端线城数限制为使用给定的核数,而不是机器的所有核数.只有为0时才使用及其的所有核数.
*
*
*/
def fromSparkConf(_conf: SparkConf, module: String, numUsableCores: Int = 0): TransportConf = {
val conf = _conf.clone
// Specify thread configuration based on our JVM's allocation of cores (rather than necessarily
// assuming we have all the machine's cores).
// NB: Only set if serverThreads/clientThreads not already set.
val numThreads = defaultNumThreads(numUsableCores)
conf.setIfMissing(s"spark.$module.io.serverThreads", numThreads.toString)
conf.setIfMissing(s"spark.$module.io.clientThreads", numThreads.toString)
new TransportConf(module, new ConfigProvider {
override def get(name: String): String = conf.get(name)
override def get(name: String, defaultValue: String): String = conf.get(name, defaultValue)
override def getAll(): java.lang.Iterable[java.util.Map.Entry[String, String]] = {
conf.getAll.toMap.asJava.entrySet()
}
})
}
// 默认的线程数
private def defaultNumThreads(numUsableCores: Int): Int = {
// 如果numUsableCores<=0 则使用系统可用的处理器数量,否则使用用户设置的数量
// 但是,不可能所有的核数都用于网络传输,所以这里有一个前提条件,就是不管是使用系统可用的核数还是用户设定的核数,都必须≤用于网络传输的核数上线(MAX_DEFAULT_NETTY_THREADS)
val availableCores =
if (numUsableCores > 0) numUsableCores else Runtime.getRuntime.availableProcessors()
math.min(availableCores, MAX_DEFAULT_NETTY_THREADS)
}
}
从fromSparkConf方法的源码中可以看到,直接通过new的方式创建了一个TransportConf对象,在TransportConf的构造方法中,传递了一个module,和一个ConfigProvider的匿名子类,在ConfigProvider的匿名子类中,实现了ConfigProvider中申明的抽象方法,可以看到,TransportConf中的配置信息也都是通过SparkConf来获取的.
在TransportConf类中,有两个成员变量以及配置项,其中,两个成员变量分别是String类型的module,和ConfigProvider,而ConfigProvider是一个抽象类,申明了两个抽象方法get和getAll以及getInt,getLong,getBoolean,getDouble几个已经实现的方法,这几个已经实现的方法都主要是通过申明的get方法来获取所需要的配置项.
TransportConf中列出的配置项以及释义(个人理解):
/**
* IO mode:O mode: nio or epoll,使用者可通过ioMode()方法获取,默认使用NIO
*/
private final String SPARK_NETWORK_IO_MODE_KEY;
/**
* 在netty层,是否使用堆外内存缓存.可通过preferDirectBufs()方法获取,如果返回是true,则使用堆外内存缓存,默认true
*/
private final String SPARK_NETWORK_IO_PREFERDIRECTBUFS_KEY;
/**
* 网络连接超时,可通过connectionTimeoutMs()方法获取.单位是毫秒,默认120*1000毫秒
*/
private final String SPARK_NETWORK_IO_CONNECTIONTIMEOUT_KEY;
/**
* 请求传入队列的对象长度,默认-1,也就是没有最大限制可通过backLog方法获取
*/
private final String SPARK_NETWORK_IO_BACKLOG_KEY;
/**
* 用于获取数据的两个节点之间的并发连接数。可用numConnectionsPerPeer方法获取.
*/
private final String SPARK_NETWORK_IO_NUMCONNECTIONSPERPEER_KEY;
/**
* 服务端线程池中使用的线程数,为核数的两倍,可通过serverThreads方法获取
*/
private final String SPARK_NETWORK_IO_SERVERTHREADS_KEY;
/**
* 客户端线程池中使用的线程数,为核数的两倍,可通过clientThreads方法获取
*/
private final String SPARK_NETWORK_IO_CLIENTTHREADS_KEY;
/**
* 接收缓冲区大小 可使用receiveBuf获取
* 注意:接收缓冲区和发送缓冲区的最佳大小应为
* 延迟*网络带宽。
* 假设延迟=1毫秒,网络带宽=10 Gbps
* 缓冲区大小应为~1.25MB
*/
private final String SPARK_NETWORK_IO_RECEIVEBUFFER_KEY;
/**
* 发送缓冲大小,可使用sendBuf获取
*/
private final String SPARK_NETWORK_IO_SENDBUFFER_KEY;
/**
* 身份验证消息交换的单次往返超时,单位毫秒,默认30*1000毫秒,可通过authRTTimeoutMs方法获取
*/
private final String SPARK_NETWORK_SASL_TIMEOUT_KEY;
/**
* 每次请求最大尝试次数,如连接超时,如果为0,将不做任何尝试,可通过maxIORetries方法获取
*/
private final String SPARK_NETWORK_IO_MAXRETRIES_KEY;
/**
* 发生IO异常后,下次尝试的等待时间,默认单位毫秒,默认5*1000毫秒,可通过ioRetryWaitTimeMs方法获取
*/
private final String SPARK_NETWORK_IO_RETRYWAIT_KEY;
/**
* 是否延迟初始化文件描述符。如果为真,则文件描述符为
* 仅在要传输数据时创建。这可以减少打开文件的数量。
* 可通过lazyFileDescriptor方法获取
*/
private final String SPARK_NETWORK_IO_LAZYFD_KEY;