spark源码阅读-存储体系3(磁盘Block管理器)

DiskBlockManager是存储体系的成员之一,它负责为逻辑的Block与数据写入磁盘的位置之间建立逻辑的映射关系。对于不了解的事务,我们应该先看看它定义了哪些属性。DiskBlockManager的属性如下。

  • conf:即sparkconf。
  • deleteFilesOnStop:停止DiskBlockManager的时候是否删除本地目录的布尔类型标记。当不指定外部的ShuffleClient(即spark.shuffle.service.enable属性为false)或当前实例是Driver时,此属性为true。
  • localDirs:本地目录的数组。

1. 本地目录结构

localDirs 就 DiskBlockManager 管理的本地目录数组。localDirs 是通过调用 createLocalDirs方法创建的本地目录数组,其实质是调用了Utils工具类的 getConfiguredLocalDirs方法获取本地路径(getConfigguredLocalDirs方法默认获取 spark.local.dir 属性或者系统属性 java.io.tmpdir指定的目录,目录可能有多个),并在每个路径下创建以 blockmgr- 为前缀,UUID为后缀的随机字符串的子目录。

//org.apache.spark.storage.DiskBlockManager
private def createLocalDirs(conf: SparkConf): Array[File] = {
  Utils.getConfiguredLocalDirs(conf).flatMap { rootDir =>
    try {
      val localDir = Utils.createDirectory(rootDir, "blockmgr")
      logInfo(s"Created local directory at $localDir")
      Some(localDir)
    } catch {
      case e: IOException =>
        logError(s"Failed to create local dir in $rootDir. Ignoring this directory.", e)
        None
    }
  }
}
  • subDirsPerLocalDir:磁盘存储DiskStore的本地子目录的数量。可以通过spark.diskStore.subDirectories属性配置,默认为64.
  • subDirs:DiskStore的本地子目录的二维数组。
  • shutdownHook:此属性的作用时初始化DiskBlockManager时,调用addShutdownHook方法(见代码清单6-19),为DiskBlockManager设置号关闭钩子。

有了对localDirs的初步了解,我们来看DiskBlockManager的文件目录结构图。

spark源码阅读-存储体系3(磁盘Block管理器)

图中一级目录名称都采用了简单的示意表示,例如Blockmgr-UUID-0、Blockmgr-UUID-1、Blockmgr-UUID-2,代表每个文件夹名称由Blockmgr- 和 UUID 生成的随机串缓存,且此随机串不相同,这里使用N代表subDirsPerLocalDir属性的大小。每个二级目录下都N个二级目录(这里的N即subDirsPerLocalDir属性的大小),有些二级目录下可能暂时还没有Block文件。 

DiskBlockManager提供的方法

1. getFile(filename: String)

根据指定的filename获取文件。

//org.apache.spark.storage.DiskBlockManager
def getFile(filename: String): File = {
  val hash = Utils.nonNegativeHash(filename)  // 调用nonNegativeHash获取文件名的非负hash值
  val dirId = hash % localDirs.length  // 用取余方法获得选中的一级目录
  val subDirId = (hash / localDirs.length) % subDirsPerLocalDir  // 获取余数作为选中的二级目录
  val subDir = subDirs(dirId).synchronized {  // 获取二级目录,若不存在则创建 
    val old = subDirs(dirId)(subDirId)
    if (old != null) {
      old
    } else {
      val newDir = new File(localDirs(dirId), "%02x".format(subDirId))
      if (!newDir.exists() && !newDir.mkdir()) {
        throw new IOException(s"Failed to create local dir in $newDir.")
      }
      subDirs(dirId)(subDirId) = newDir
      newDir  // 返回耳机目录下的文件
    }
  }

2.getFile(blockId:BlockId)

def getFile(blockId:BlockId): File = getFile(blockId.name) // 根据blockId的name获取文件

3. containsBlock

此方法用于检查本地localDirs目录中是否含有BlockId对应的文件

def containsBlock(blockId: BlockId): Boolean = {
  getFile(blockId.name).exists()
}

4. getAllFiles

此方法用于获取本地localDirs目录中的所有文件。

def getAllFiles(): Seq[File] = {
  subDirs.flatMap { dir =>
    dir.synchronized {
      dir.clone()  // 同步加克隆,保证线程安全
    }
  }.filter(_ != null).flatMap { dir =>
    val files = dir.listFiles()
    if (files != null) files else Seq.empty
  }
}

5. getAllBlocks

此方法用于获取本地localDirs目录中所有Block的BlockId

def getAllBlocks(): Seq[BlockId] = {
  getAllFiles().map(f => BlockId(f.getName))
}

6. createTempLocalBlock

此方法用于为中间结果创建唯一的BlockId和文件,此文件用于保存本地Block数据。

def createTempLocalBlock(): (TempLocalBlockId, File) = {
  var blockId = new TempLocalBlockId(UUID.randomUUID())
  while (getFile(blockId).exists()) {
    blockId = new TempLocalBlockId(UUID.randomUUID())
  }
  (blockId, getFile(blockId))
}

7. createTempShuffleBlock

此方法创建唯一的BlockId和文件,用来存储Shuffle的中间结果(即map任务的输出)。

def createTempShuffleBlock(): (TempShuffleBlockId, File) = {
  var blockId = new TempShuffleBlockId(UUID.randomUUID())
  while (getFile(blockId).exists()) {
    blockId = new TempShuffleBlockId(UUID.randomUUID())
  }
  (blockId, getFile(blockId))
}

8. stop

此方法用于正常停止DiskBlockManager。

private[spark] def stop() {
  // Remove the shutdown hook.  It causes memory leaks if we leave it around.
  try {
    ShutdownHookManager.removeShutdownHook(shutdownHook)  // 移除钩子
  } catch {
    case e: Exception =>
      logError(s"Exception while removing shutdown hook.", e)
  }
  doStop()
}

从上述代码可以看出,stop实际执行的方法是doStop,其实现如下。

private def doStop(): Unit = {
  if (deleteFilesOnStop) {
    localDirs.foreach { localDir =>  // 遍历一级目录
      if (localDir.isDirectory() && localDir.exists()) { 
        try {
          if (!ShutdownHookManager.hasRootAsShutdownDeleteDir(localDir)) {
            Utils.deleteRecursively(localDir)  // 调用deleteRecursively递归删除一级目录下的所有内容
          }
        } catch {
          case e: Exception =>
            logError(s"Exception while deleting local spark dir: $localDir", e)
        }
      }
    }
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Spark submit任务提交是指将用户编写的Spark应用程序提交到集群中运行的过程。在Spark中,用户可以通过命令行工具或API方式提交任务。 Spark submit命令的基本语法如下: ``` ./bin/spark-submit \ --class <main-class> \ --master <master-url> \ --deploy-mode <deploy-mode> \ --conf <key>=<value> \ <application-jar> \ [application-arguments] ``` 其中,`--class`指定应用程序的主类,`--master`指定集群的URL,`--deploy-mode`指定应用程序的部署模式,`--conf`指定应用程序的配置参数,`<application-jar>`指定应用程序的jar包路径,`[application-arguments]`指定应用程序的命令行参数。 在Spark中,任务提交的过程主要包括以下几个步骤: 1. 创建SparkConf对象,设置应用程序的配置参数; 2. 创建SparkContext对象,连接到集群; 3. 加载应用程序的主类; 4. 运行应用程序的main方法; 5. 关闭SparkContext对象,释放资源。 在任务提交的过程中,Spark会自动将应用程序的jar包和依赖的库文件上传到集群中,并在集群中启动Executor进程来执行任务。任务执行完成后,Spark会将结果返回给Driver进程,并将Executor进程关闭。 总之,Spark submit任务提交是Spark应用程序运行的关键步骤,掌握任务提交的原理和方法对于开发和调试Spark应用程序非常重要。 ### 回答2: Spark 作为一款强大的分布式计算框架,提供了很多提交任务的方式,其中最常用的方法就是通过 spark-submit 命令来提交任务。spark-submit 是 Spark 提供的一个命令行工具,用于在集群上提交 Spark 应用程序,并在集群上运行。 spark-submit 命令的语法如下: ``` ./bin/spark-submit [options] <app jar | python file> [app arguments] ``` 其中,[options] 为可选的参数,包括了执行模式、执行资源等等,<app jar | python file> 为提交的应用程序的文件路径,[app arguments] 为应用程序运行时的参数。 spark-submit 命令会将应用程序的 jar 文件以及所有的依赖打包成一个 zip 文件,然后将 zip 文件提交到集群上运行。在运行时,Spark 会根据指定的主类(或者 Python 脚本文件)启动应用程序。 在提交任务时,可以通过设置一些参数来控制提交任务的方式。例如: ``` --master:指定该任务运行的模式,默认为 local 模式,可设置为 Spark Standalone、YARN、Mesos、Kubernetes 等模式。 --deploy-mode:指定该任务的部署模式,默认为 client,表示该应用程序会在提交任务的机器上运行,可设置为 cluster,表示该应用程序会在集群中一台节点上运行。 --num-executors:指定该任务需要的 executor 数量,每个 executor 会占用一个计算节点,因此需要根据集群配置与任务要求确定该参数的值。 --executor-memory:指定每个 executor 可用的内存量,默认为 1g,可以适当调整该值以达到更好的任务运行效果。 ``` 此外,还有一些参数可以用来指定应用程序运行时需要传递的参数: ``` --conf:指定应用程序运行时需要的一些配置参数,比如 input 文件路径等。 --class:指定要运行的类名或 Python 脚本文件名。 --jars:指定需要使用的 Jar 包文件路径。 --py-files:指定要打包的 python 脚本,通常用于将依赖的 python 包打包成 zip 文件上传。 ``` 总之,spark-submit 是 Spark 提交任务最常用的方法之一,通过该命令能够方便地将应用程序提交到集群上运行。在提交任务时,需要根据实际场景调整一些参数,以达到更好的任务运行效果。 ### 回答3: Spark是一个高效的分布式计算框架,其中比较重要的组成部分就是任务提交。在Spark中,任务提交主要通过spark-submit来实现。本文将从两方面,即任务提交之前的准备工作和任务提交过程中的细节进行探讨。 一、任务提交之前的准备工作 1.环境配置 在执行任务提交前,需要确保所在的计算机环境已经配置好了SparkSpark的环境配置主要包括JAVA环境、Spark的二进制包、PATH路径配置、SPARK_HOME环境变量配置等。 2.编写代码 Spark的任务提交是基于代码的,因此在任务提交前,需要编写好自己的代码,并上传到集群中的某个路径下,以便后续提交任务时调用。 3.参数设置 在任务提交时,需要对一些关键的参数进行设置。例如,任务名、任务对应的代码路径、任务需要的资源、任务需要的worker节点等。 二、任务提交过程中的细节 1.启动Driver 当使用spark-submit命令提交任务时,Spark会启动一个Driver来运行用户的代码。这个Driver通常需要连接到Spark集群来执行任务。 2.上传文件 Spark支持在任务提交时上传所需的文件。这些文件可以用于设置Spark的环境变量、为任务提供数据源等。 3.资源需求 Spark的任务执行依赖于一定的资源。每个任务可以指定自己的资源需求,例如需要多少内存、需要多少CPU等。这些资源需求通常与提交任务时需要的worker节点数量有关系。 4.监控和日志 在任务执行的过程中,Spark会收集任务的监控数据和日志信息。这些数据可用于后续的调试和性能优化。 总之,在Spark任务提交过程中,需要充分考虑任务的资源需求和监控日志信息的收集,以便更好地完成任务和优化Spark运行效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值