Spark源码分析之Spark执行环境SparkEnv

SparkEnv是创建SparkContext中的第一步,也是很重要的一环,它Spark 的执行环境对象,其中包括众多与Executor 执行相关的对象。

由于在local 模式下Driver 会创建Executor,cluster 部署模式或者Standalone 部署模式下Worker 另起的CoarseGrainedExecutorBackend 进程中也会创建Executor,所以SparkEnv 存在于Driver 或者CoarseGrainedExecutorBackend 进程中,创建SparkEnv 主要使用SparkEnv类 的createDriverEnv,

private[spark] def createSparkEnv(
      conf: SparkConf,
      isLocal: Boolean,
      listenerBus: LiveListenerBus): SparkEnv = {
    SparkEnv.createDriverEnv(conf, isLocal, listenerBus, SparkContext.numDriverCores(master))
  }

可以看到,传入的参数包括listerBus,所以在创建SparkEnv之前,要先创建JobProgressListener(application运行信息记录器)。

 _jobProgressListener = new JobProgressListener(_conf)
    listenerBus.addListener(jobProgressListener)

其实,createDriverEnv()最终调用的还是SparkEnv.create()创建SparkEnv。SparkEnv的构造步骤如下。

1. 创建安全管理器SecurityManager;
2. 创建Map 任务输出跟踪器mapOutputTracker;
3. 实例化ShuffleManager;
4. 创建MemoryManager;
5. 创建块传输服务BlockTransferService;
6. 创建BlockManagerMaster;
7. 创建块管理器BlockManager;
8. 创建广播管理器BroadcastManager;
9. 创建缓存管理器CacheManager;
10. 创建测量系统MetricsSystem;
11. 创建SparkEnv。
安全管理器SecurityManager

SecurityManager 主要对权限、账号进行设置,如果使用Hadoop YARN 作为集群管理器,则需要使用证书生成 secret key 登录,最后给当前系统设置默认的口令认证实例,此实例采用匿名内部类实现

/*
  使用http 设置认证口令
*/
if (authOn) {
    Authenticator.setDefault(
      new Authenticator() {
        override def getPasswordAuthentication(): PasswordAuthentication = {
          var passAuth: PasswordAuthentication = null
          val userInfo = getRequestingURL().getUserInfo()
          if (userInfo != null) {
            val  parts = userInfo.split(":", 2)
            passAuth = new PasswordAuthentication(parts(0), parts(1).toCharArray())
          }
          return passAuth
        }
      }
    )
  }
map 任务输出跟踪器mapOutputTracker

mapOutputTracker 用于跟踪map 阶段任务的输出状态,此状态便于reduce 阶段任务获取地址及中间输出结果。每个map 任务或者reduce 任务都会有其唯一标识,分别为mapId 和reduceId。每个reduce 任务的输入可能是多个map 任务的输出,reduce 会到各个map 任务的所在节点上拉取Block,这一过程叫做shuffle。每批shuffle 过程都有唯一的标识shuffleId。

这里先介绍下MapOutputTrackerMaster。MapOutputTrackerMaster 内部使用mapStatuses:TimeStampedHashMap[Int, Array[MapStatus]] 来维护跟踪各个map 任务的输出状态。其中key对应shuffleId,Array 存储各个map 任务对应的状态信息MapStatus。由于MapStatus 维护了map 输出Block 的地址BlockManagerId,所以reduce 任务知道从何处获取map 任务的中间输出MapOutputTrackerMaster 还使用cachedSerializedStatuses :TimeStampedHashMap[Int,Array[Byte]] 维护序列化后的各个map 任务的输出状态。其中key 对应shuffleId,Array 存储各个序列化MapStatus 生成的字节数组。

/**
 * Timestamp based HashMap for storing mapStatuses and cached serialized statuses in the driver,
 * so that statuses are dropped only by explicit de-registering or by TTL-based cleaning (if set).
 * Other than these two scenarios, nothing should be dropped from this HashMap.
   */
  protected val mapStatuses = new TimeStampedHashMap[Int, Array[MapStatus]]()
  private val cachedSerializedStatuses = new TimeStampedHashMap[Int, Array[Byte]]()

Driver 和Executor 处理MapOutputTrackerMaster 的方式有所不同。

  • 如 果 当 前 应 用 程 序 是 Driver, 则 创 建 MapOutputTrackerMaster, 然 后 创建MapOutputTrackerMasterEndpoint,并注册到rpcEnv中。
  • 如果当前应用程序是 Executor,则创建 MapOutputTrackerWorker,并从 rpcEnv中找到MapOutputTrackerMasterEndpoint。

无论是Driver 还是Executor, 最后都由mapOutputTracker 的属性trackerEndpoint持有
MapOutputTrackerMasterActor 的引用RpcEndpointRef。

def registerOrLookupEndpoint(
        name: String, endpointCreator: => RpcEndpoint):
      RpcEndpointRef = {
      if (isDriver) {
        logInfo("Registering " + name)
        rpcEnv.setupEndpoint(name, endpointCreator)
      } else {
        RpcUtils.makeDriverRef(name, conf, rpcEnv)
      }
    }

val mapOutputTracker = if (isDriver) {
      new MapOutputTrackerMaster(conf)
    } else {
      new MapOutputTrackerWorker(conf)
    }

mapOutputTracker.trackerEndpoint = registerOrLookupEndpoint(MapOutputTracker.ENDPOINT_NAME,
      new MapOutputTrackerMasterEndpoint(
        rpcEnv, mapOutputTracker.asInstanceOf[MapOutputTrackerMaster], conf))

map 任务的状态正是由Executor 向持有的MapOutputTrackerMasterEndpoint发送消息, 将map 任务状态同步到mapOutputTracker 的mapStatuses 和cachedSerializedStatuses 的。

实例化ShuffleManager

ShuffleManager 负责管理本地及远程的block 数据的shuffle 操作。ShuffleManager默认为通过反射方式生成的SortShuffleManager 的实例, 可以修改属性spark.shuffle.manager 为hash 来显式控制使用HashShuffleManager

val shortShuffleMgrNames = Map(
      "hash" -> "org.apache.spark.shuffle.hash.HashShuffleManager",
      "sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager",
      "tungsten-sort" -> "org.apache.spark.shuffle.sort.SortShuffleManager")
    val shuffleMgrName = conf.get("spark.shuffle.manager", "sort")
    val shuffleMgrClass = shortShuffleMgrNames.getOrElse(shuffleMgrName.toLowerCase, shuffleMgrName)
    val shuffleManager = instantiateClass[ShuffleManager](shuffleMgrClass)
内存管理器MemoryManager

在Spark-1.6.0中,引入了一个新的参数spark.memory.userLegacyMode(默认值为false),表示不使用Spark-1.6.0之前的内存管理机制,而是使用1.6.0中引入的动态内存分配这一概念,主要影响到构造memoryManager的类的不同。

val useLegacyMemoryManager = conf.getBoolean("spark.memory.useLegacyMode", false)
    val memoryManager: MemoryManager =
      if (useLegacyMemoryManager) {
        new StaticMemoryManager(conf, numUsableCores)
      } else {
        UnifiedMemoryManager(conf, numUsableCores)
      }

StaticMemoryManager是Spark-1.6.0之前版本中主要使用的,对各部分内存静态划分好后便不可变化。
而useLegacyMemoryManager ,根据其注释看一看到。

该memoryManager主要是使得execution部分和storage部分的内存不像之前由比例参数限定住,而是两者可以互相借用内存。execution和storage总的内存上限由参数`spark.memory.fraction(默认0.75)来设定的,这个比例是相对于整个JVM heap来说的。
Storage部分可以申请Execution部分的所有空闲内存,直到Execution内存不足时向Storage发出信号为止。当Execution需要更多内存时,Storage部分会向磁盘spill数据,直到把借用的内存都还上为止。
同样的Execution部分也能向Storage部分借用内存,当Storage需要内存时,Execution中的数据不会马上spill到磁盘,因为Execution使用的内存发生在计算过程中,如果数据丢失就会到账task计算失败。Storage部分只能等待Execution部分主动释放占用的内存。

块传输服务BlockTransferService

BlockTransferService 默认为NettyBlockTransferService,它使用Netty 提供的异步事件驱动的网络
应用框架,提供web 服务及客户端,获取远程节点上Block 的集合。

val blockTransferService = new NettyBlockTransferService(conf, securityManager, numUsableCores)
BlockManagerMaster

BlockManagerMaster 负责对Block 的管理和协调, 具体操作依赖于BlockManagerMasterActor。Driver 和Executor 处理BlockManagerMaster 的方式不同:

val blockManagerMaster = new BlockManagerMaster(registerOrLookupEndpoint(
      BlockManagerMaster.DRIVER_ENDPOINT_NAME,
      new BlockManagerMasterEndpoint(rpcEnv, isLocal, conf, listenerBus)),
      conf, isDriver)

与mapOutputTracker类似。

  • 如果当前应用程序是Driver,则创建 BlockManagerMasterRpcEndPoint,并且注册到 RpcEnv中
  • 如果当前应用程序是 Executor,则从 RpcEnv中找到 BlockManagerMasterRpcEndPoint

无论是Driver 还是Executor, 最后BlockManagerMaster 的属性driverEndPoint 将持有对
BlockManagerMasterRpcEndPoint 的引用RpcEndpointRef。

class BlockManagerMaster(
    var driverEndpoint: RpcEndpointRef,
    conf: SparkConf,
    isDriver: Boolean)
  extends Logging {
创建块管理器BlockManager

BlockManager 负责对Block 的管理, 是存储系统的一部分只有在BlockManager 的初始化方法initialize
被调用后,它才是有效的。

// NB: blockManager is not valid until initialize() is called later.
    val blockManager = new BlockManager(executorId, rpcEnv, blockManagerMaster,
      serializer, conf, memoryManager, mapOutputTracker, shuffleManager,
      blockTransferService, securityManager, numUsableCores)
创建广播管理器BroadcastManager

BroadcastManager 用于将配置信息和序列化后的RDD、Job 以及ShuffleDependency 等信息
在本地存储。如果为了容灾,也会复制到其他节点上。

val broadcastManager = new BroadcastManager(isDriver, conf, securityManager)

BroadcastManager 必须在其初始化方法initialize 被调用后,才能生效。initialize 方法实际利用反射生成广播工厂实例broadcastFactory(可以配置属性spark.broadcast.factory 指定, 默认org.apache.spark.broadcast.TorrentBroadcastFactory)。BroadcastManager 的广播方法newBroadcast 实际代理了工厂broadcastFactory 的newBroadcast 方法来生成广播对象。unbroadcast 方法实际代理了工厂broadcastFactory 的unbroadcast 方法生成非广播对象。

private def initialize() {
    synchronized {
      if (!initialized) {
        val broadcastFactoryClass =
          conf.get("spark.broadcast.factory", "org.apache.spark.broadcast.TorrentBroadcastFactory")

        broadcastFactory =
          Utils.classForName(broadcastFactoryClass).newInstance.asInstanceOf[BroadcastFactory]

        // Initialize appropriate BroadcastFactory and BroadcastObject
        broadcastFactory.initialize(isDriver, conf, securityManager)

        initialized = true
      }
    }
  }

def newBroadcast[T: ClassTag](value_ : T, isLocal: Boolean): Broadcast[T] = {
    broadcastFactory.newBroadcast[T](value_, isLocal, nextBroadcastId.getAndIncrement())
  }

  def unbroadcast(id: Long, removeFromDriver: Boolean, blocking: Boolean) {
    broadcastFactory.unbroadcast(id, removeFromDriver, blocking)
  }
创建缓存管理器CacheManager

CacheManager 用于缓存RDD 某个分区计算后的中间结果,缓存计算结果发生在迭代计算的时候。

测量系统MetricsSystem
val metricsSystem = if (isDriver) {
      // Don't start metrics system right now for Driver.
      // We need to wait for the task scheduler to give us an app ID.
      // Then we can start the metrics system.
      MetricsSystem.createMetricsSystem("driver", conf, securityManager)
    } else {
      // We need to set the executor ID before the MetricsSystem is created because sources and
      // sinks specified in the metrics configuration file will want to incorporate this executor
      // ID into the metrics they report.
      conf.set("spark.executor.id", executorId)
      val ms = MetricsSystem.createMetricsSystem("executor", conf, securityManager)
      ms.start()
      ms
    }

构造MetricsSystem 的过程最重要的是调用了MetricsConfig 的initialize 方法.

def initialize() {
    // Add default properties in case there's no properties file
    setDefaultProperties(properties)

    loadPropertiesFromFile(conf.getOption("spark.metrics.conf"))

    // Also look for the properties in provided Spark configuration
    val prefix = "spark.metrics.conf."
    conf.getAll.foreach {
      case (k, v) if k.startsWith(prefix) =>
        properties.setProperty(k.substring(prefix.length()), v)
      case _ =>
    }

    propertyCategories = subProperties(properties, INSTANCE_REGEX)
    if (propertyCategories.contains(DEFAULT_PREFIX)) {
      val defaultProperty = propertyCategories(DEFAULT_PREFIX).asScala
      for((inst, prop) <- propertyCategories if (inst != DEFAULT_PREFIX);
          (k, v) <- defaultProperty if (prop.get(k) == null)) {
        prop.put(k, v)
      }
    }
  }
创建SparkEnv

当所有的基础组件准备好后,开始创建SparkEnv

val envInstance = new SparkEnv(
      executorId,
      rpcEnv,
      actorSystem,
      serializer,
      closureSerializer,
      cacheManager,
      mapOutputTracker,
      shuffleManager,
      broadcastManager,
      blockTransferService,
      blockManager,
      securityManager,
      sparkFilesDir,
      metricsSystem,
      memoryManager,
      outputCommitCoordinator,
      conf)

之所以并没有介绍actorSystem,是因为在spark1.6中已经把actorSystem移除了。

  _actorSystem: ActorSystem, // TODO Remove actorSystem

至此,SparkEnv创建完毕!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值