前言
最近有个spark程序因为资源不足以及其他原因会在第一次提交时候失败,然后又会不断提交,导致过多的系统资源被无效占用。因此想限制Spark作业失败的重试次数,如果第一次失败,就让作业直接失败,那么该具体该如何实现呢?
解决方法
首先查看了spark的属性配置,发现我们使用spark.yarn.maxAppAttempts属性在提交程序时限制其重试次数,如:
spark-submit --conf spark.yarn.maxAppAttempts=1
该属性在Spark源代码中的使用如下:
//org.apache.spark.deploy.yarn.config.scala
private[spark] val MAX_APP_ATTEMPTS = ConfigBuilder("spark.yarn.maxAppAttempts")
.doc("Maximum number of AM attempts before failing the app.")
.intConf
因为我们使用的是Spark on Yarn,虽然由Yarn负责启动和管理AM以及分配资源,但是Spark有自己的AM实现,当Executor运行起来后,任务的控制是由Driver负责的。而重试上,Yarn只负责AM的重试。
另外,在Spark对ApplicationMaster的实现里,Spark提供了参数 spark.yarn.max.executor.failures 来控制Executor的失败次数,当Executor的失败次数达到这个值的时候,整个Spark应该程序就失败了,判断逻辑如下:
//org.apache.spark.deploy.yarn.ApplicationMaster.scala
private val maxNumExecutorFailures = {
val effectiveNumExecutors =
if (Utils.isStreamingDynamicAllocationEnabled(sparkConf)) {
sparkConf.get(STREAMING_DYN_ALLOCATION_MAX_EXECUTORS)
} else if (Utils.isDynamicAllocationEnabled(sparkConf)) {
sparkConf.get(DYN_ALLOCATION_MAX_EXECUTORS)
} else {
sparkConf.get(EXECUTOR_INSTANCES).getOrElse(0)
}
// By default, effectiveNumExecutors is Int.MaxValue if dynamic allocation is enabled. We need
// avoid the integer overflow here.
val defaultMaxNumExecutorFailures = math.max(3,
if (effectiveNumExecutors > Int.MaxValue / 2) Int.MaxValue else (2 * effectiveNumExecutors))
sparkConf.get(MAX_EXECUTOR_FAILURES).getOrElse(defaultMaxNumExecutorFailures)
}
以上相关Spark属性的定义如下:
属性名 | 默认值 | 解释 |
---|---|---|
spark.yarn.maxAppAttempts | YARN配置中的yarn.resourcemanager.am.max-attempts属性的值 | 提交申请的最大尝试次数, 小于等于YARN配置中的全局最大尝试次数。 |
spark.yarn.max.executor.failures | numExecutors * 2, with minimum of 3 | 应用程序失败之前的最大执行程序失败次数。 |
而在YARN配置中,我们可以看到:
属性名 | 默认值 | 解释 |
---|---|---|
yarn.resourcemanager.am.max-attempts | 2 | 最大应用程序尝试次数。 它是所有AM的全局设置。 每个应用程序主机都可以通过API指定其各自的最大应用程序尝试次数,但是单个数字不能超过全局上限。 如果是,资源管理器将覆盖它。 默认数量设置为2,以允许至少一次重试AM. |
所以默认情况下,spark.yarn.maxAppAttempts的值为2,如果想不进行第二次重试,可以将改值设为1(注意,0值是无效的,至少为提交一次)