1003检查点checkPoint的实现

检查点是很多分布式系统为了容灾容错引入的机制,其实质是将系统运行期的内存数据结构和状态持久化到磁盘上,在需要时通过对这些持久化数据的读取,重新构造出之前的运行期状态。Spark 使用检查点主要是为了将 RDD 的执行状态保留下来,在重新执行时就不用重新计算,而直接从检查点读取。CheckpointRDD 是对检查点数据进行操作的 RDD,例如,读写检查点数据。RDDCheckpointData 表示 RDD 检查点的数据。Spark 的检查点离不开 CheckpointRDD 和 RDDCheckpointData 的支持,本节将对它们的代码实现进行分析。

 

CheckpointRDD 的实现

CheckpointRDD 是特殊的 RDD用来从存储体系中恢复检查点数据CheckpointRDD 的定义如下

private[spark] abstract class CheckpointRDD[T: ClassTag](sc: SparkContext)
  extends RDD[T](sc, Nil) {
  override def doCheckpoint(): Unit = { }
  override def checkpoint(): Unit = { }
  override def localCheckpoint(): this.type = this
  protected override def getPartitions: Array[Partition] = ???
  override def compute(p: Partition, tc: TaskContext): Iterator[T] = ???
}

 

可以看到 CheckpointRDD 重写了 RDD 的 5 个方法,分别如下。

 

  • doCheckpoint:保存检查点。
  • checkpoint:读取检查点数据。
  • localCheckpoint:本地的检查点。
  • getPartitions:获取检查点的分区数组。
  • compute:名为计算,实际是从检查点恢复数据。

CheckpointRDD 有 LocalCheckpointRDD 和 ReliableCheckpointRDD 两个子类,它们都没有完全实现 CheckpointRDD 中的方法。下面以 ReliableCheckpointRDD为例,来介绍 Check-pointRDD 的具体实现。

 LocalCheckpointRDD 

ReliableCheckpointRDD

ReliableCheckpointRDD 中定义了以下属性。

  • sc:即 SparkContext。
  • checkpointPath: 检查点目录的字符串表示。
  • _partitioner: 调用方指定的分区计算器,默认为 None。
  • hadoopConf: SparkContext 的_hadoopConfiguration 属性,即 Hadoop 的配置信息。
  • cpath: 类型为 org.apache.hadoop.fs.Path,表示 checkpointPath 对应的 Hadoop 文件系统中的路径。
  • fs: 使用 hadoopConf 得到的 org.apache.hadoop.fs.FileSystem。
  • broadcastedConf: 调用 SparkContext 的 broadcast 方法对 hadoopConf 进行广播后返回的 Broadcast 对象。
  • partitioner: ReliableCheckpointRDD 的分区计算器,优先采用_partitioner指定的,否则调用 ReliableCheckpointRDD 的伴生对象的readCheckpointedPartitionerFile 方法从 checkpointPath 指定的检查点目录下读取分区计算器。

ReliableCheckpointRDD 的伴生对象中提供了很多工具方法,下面逐个介绍。

writePartitionToCheckpointFile方法

writePartitionToCheckpointFile 方法用于将 RDD 分区的数据写入到检查点目录下的文件中

writePartitionerToCheckpointDir方法

writePartitionerToCheckpointDir 方法用于将分区计算器的数据写入到检查点的目录下

writeRDDToCheckpointDirectory方法

writeRDDToCheckpointDirectory 方法用于将 RDD 的数据写入检查点目录

riteRDDToCheckpointDirectory 方法的执行步骤如下。

  1. 调用 SparkContext 的 runJob 方法将 RDD 的数据写入到检查点目录。将数据写入磁盘的函数是 ReliableCheckpointRDD 的伴生对象的writePartitionToCheckpointFile 方法
  2. 如果 RDD 有分区计算器,那么调用 ReliableCheckpointRDD 的伴生对象的write-PartitionerToCheckpointDir 方法将分区计算器的信息也写入到检查点目录。
  3. 创建并返回 ReliableCheckpointRDD。

readCheckpointFile方法

readCheckpointFile 方法用于从检查点目录下的文件中读取 RDD 的数据

上面介绍了 ReliableCheckpointRDD 的伴生对象提供的方法下面介绍ReliableCheck-pointRDD 对父类 RDD 重写的部分方法

getPartitions方法

ReliableCheckpointRDD 实现的 getPartitions 方法用于从检查点文件中获取分区数组

getPreferredLocations方法

ReliableCheckpointRDD 实现的 getPreferredLocations 方法用于从检查点文件中获取偏好位置

compute方法

ReliableCheckpointRDD 实现的 compute 方法用于从检查点文件中获取检查点数据

override def compute(split: Partition, context: TaskContext): Iterator[T] = {
  val file = new Path(checkpointPath, ReliableCheckpointRDD.checkpointFile-Name(split.index))
  ReliableCheckpointRDD.readCheckpointFile(file, broadcastedConf, context)
}

根据对 CheckpointRDD 的各个方法的分析检查点似乎不再那么神秘其实现不过是对文件的读与写

 

RDDCheckpointData 的实现

RDDCheckpointData 用于保存与检查点相关的信息。每个 RDDCheckpointData实例都与一个 RDD 实例相关联。RDDCheckpointData 中一共有三个属性。

  • rdd:RDDCheckpointData 关联的 RDD。
  • cpState:检查点的状态。默认为 Initialized。cpState 的值来自枚举类型Checkpoint-State,CheckpointState 中定义了检查点的状态,包括初始化完成(Initialized)、正在保存检查点(CheckpointingInProgress)和保存检查点完毕(Checkpointed)。
  • cpRDD:保存检查点数据的 RDD,即 CheckpointRDD 的实现类。

RDDCheckpointData 中定义了一些方法,分别如下。

isCheckpointed方

isCheckpointed 方法用于判断是否已经为 RDDCheckpointData 关联的 RDD 保存了检查点数据其实现如下

def isCheckpointed: Boolean = RDDCheckpointData.synchronized {
  cpState == Checkpointed
}

checkpoint

heckpoint 方法是用于将 RDDCheckpointData 关联的 RDD 的数据保存到检查点的模板方法

final def checkpoint(): Unit = {
  RDDCheckpointData.synchronized {
    if (cpState == Initialized) {
      cpState = CheckpointingInProgress
    } else {
      return
    }
  }
  val newRDD = doCheckpoint()
  // Update our state and truncate the RDD lineage
  RDDCheckpointData.synchronized {
    cpRDD = Some(newRDD)
    cpState = Checkpointed
    rdd.markCheckpointed()
  }
}

checkpoint 方法的执行步骤如下。

  1. 如果检查点的状态是 Initialized,那么将 cpState 设置为CheckpointingInProgress,否则返回。
  2. 调用 doCheckpoint 方法保存检查点并生成 CheckpointRDD。doCheckpoint方法需要 RDDCheckpointData 的子类实现。RDDCheckpointData 的子类有LocalRDDCheckpointData 和 ReliableRDDCheckpointData 两种,10.3.3 节以 ReliableRDDCheckpointData 为例,介绍了其实现的 doCheckpoint 方法。
  3. 由 cpRDD 持有刚生成的 CheckpointRDD,然后将 cpState 设置为Checkpointed,最后调用 RDD 的 markCheckpointed 方法(见代码清单 10-12)清空依赖。之所以清空依赖,是因为现在已经有了 CheckpointRDD,之前的依赖关系不再需要了。

标记 RDD 已经保存了检查点

private[spark] def markCheckpointed(): Unit = {
  clearDependencies()
  partitions_ = null
  deps = null    // Forget the constructor argument for dependencies too
}

protected def clearDependencies() {
  dependencies_ = null
}

checkpointRDD

checkpointRDD 方法用于获取 cpRDD 持有的 CheckpointRDD其实现如下

def checkpointRDD: Option[CheckpointRDD[T]] = RDDCheckpointData.synchronized { cpRDD }

getPartitions

getPartitions 方法用于获取 CheckpointRDD 的分区数组

def getPartitions: Array[Partition] = RDDCheckpointData.synchronized {
  cpRDD.map(_.partitions).getOrElse { Array.empty }
}

有了对 RDDCheckpointData 的理解下一小节以 RDDCheckpointData 的子类Reliable-RDDCheckpointData 为例来看看 RDDCheckpointData 该如何实现

 

ReliableRDDCheckpointData 的实现

本节以 RDDCheckpointData 的子类 ReliableRDDCheckpointData 为例,来看看RDD-CheckpointData 的具体实现。

ReliableRDDCheckpointData 除继承了 RDDCheckpointData 的属性外,还有自身的一个属性 cpDir。cpDir 是保存 ReliableRDDCheckpointData 所关联的 RDD 数据的检查点目录,是通过调用 ReliableRDDCheckpointData 的伴生对象的checkpointPath 方法生成的。

ReliableRDDCheckpointData 及其伴生对象提供了以下方法。

checkpointPath

checkpointPath 方法用于在 SparkContext checkpointDir 属性指定的 RDD 计算过程中保存检查点的目录下创建子目录作为保存 ReliableRDDCheckpointData所关联的 RDD 数据的检查点目录ReliableRDDCheckpointData 的伴生对象提供的 checkpointPath 方法

def checkpointPath(sc: SparkContext, rddId: Int): Option[Path] = {
  sc.checkpointDir.map { dir => new Path(dir, s"rdd-$rddId") }
}

doCheckpoint

ReliableRDDCheckpointData 实现了父类 RDDCheckpointData 定义的doCheckpoint 方法如代码

protected override def doCheckpoint(): CheckpointRDD[T] = {
  val newRDD = ReliableCheckpointRDD.writeRDDToCheckpointDirectory(rdd, cpDir)

  if (rdd.conf.getBoolean("spark.cleaner.referenceTracking.cleanCheckpoints", false)) {
    rdd.context.cleaner.foreach { cleaner =>
      cleaner.registerRDDCheckpointDataForCleanup(newRDD, rdd.id)
    }
  }

  logInfo(s"Done checkpointing RDD ${rdd.id} to $cpDir, new parent is RDD ${newRDD.id}")
  newRDD
}

ReliableRDDCheckpointData 实现的 doCheckpoint 方法的执行步骤如下。

  1. 调用 ReliableCheckpointRDD 的伴生对象的writeRDDToCheckpointDirectory 方法(见代码清单 10-6)将 RDD 的数据保存到检查点目录。
  2. 如果 spark.cleaner.referenceTracking.cleanCheckpoints 属性指定为 true,那么将生成的 ReliableCheckpointRDD 注册到 SparkContext 的子组件ContextCleaner 的 referenceBuffer 中,以便于 ContextCleaner 对ReliableCheckpointRDD 进行清理。
  3. 返回生成的 ReliableCheckpointRDD。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值