spark脚本监控任务运行状态

博客围绕根据appName监控Spark任务展开。业务需求是实现对Spark任务是否存在及是否卡死的监控,若任务不存在则启动,存在且长时间无活动则杀掉等待重启。介绍了具体实现步骤,包括验证任务存在性、读取监控文件计算时间差等,还提及监控脚本业务流程和监控文件生成。

如何根据appName监控spark任务,当任务不存在则启动(任务存在当超过多久没有活动状态则kill,等待下次启动)

业务需求

实现一个根据spark任务的appName来监控任务是否存在,及任务是否卡死的监控。

1)给定一个appName,根据appName从yarn application -list中验证任务是否存在,不存在则调用spark-submit.sh脚本来启动任务;

2)如果任务存在yarn application -list中,则读取‘监控文件(监控文件内容包括:appId,最新活动时间)’,从监控文件中读取出最后活动的日期,计算当前日期与app的最后活动日期相差时间为X,如果X大于30minutes(认为任务处于假死状态[再发布环境发现有的任务DAG抛出OOM,导致app的executor和driver依然存在,当时不执行任务调度,程序卡死。具体错误详情请参考《https://issues.apache.org/jira/browse/SPARK-26452》]),则执行yarn application -kill appId(杀掉任务),等待下次监控脚本执行时重启任务。

监控实现
脚本

#/bin/sh
#LANG=zh_CN.utf8
#export LANG
export SPARK_KAFKA_VERSION=0.10
export LANG=zh_CN.UTF-8
# export env variable
if [ -f ~/.bash_profile ];
then
   source ~/.bash_profile
fi
source /etc/profile

myAppName='myBatchTopic'               #这里指定需要监控的spark任务的appName,注意:这名字重复了会导致监控失败。
apps=''

for app in `yarn application -list`
do
  apps=${app},$apps
done
apps=${apps%?}

if [[ $apps =~ $myAppName ]];
then
  echo "appName($myAppName) exists in yarn application list"
  #1)运行 hadop fs -cat /目录/appName,读取其中最后更新日期;(如果文件不存在,则跳过等待文件生成。)
  monitorInfo=$(hadoop fs -cat /user/dx/streaming/monitor/${myAppName})
  LD_IFS="$IFS"
  IFS=","
  array=($monitorInfo)
  IFS="$OLD_IFS"  
  appId=${array[0]}
  monitorLastDate=${array[1]}
  echo "loading mintor information 'appId:$appId,monitorLastUpdateDate:$monitorLastDate'"

  current_date=$(date "+%Y-%m-%d %H:%M:%S")
  echo "loading current date '$current_date'"
  
  #2)与当前日期对比:
  # 如果距离当前日期相差小于30min,则不做处理;
  # 如果大于30min则kill job,根据上边yarn application -list中能获取对应的appId,运行yarn application -kill appId
  t1=`date -d "$current_date" +%s`
  t2=`date -d "$monitorLastDate" +%s`
  diff_minute=$(($(($t1-$t2))/60))
  echo "current date($current_date) over than monitorLastDate($monitorLastDate) $diff_minute minutes"
  if [ $diff_minute -gt 30 ];
  then
    echo 'over then 30 minutes'
    $(yarn application -kill ${appId})
    echo "kill application ${appId}"
  else
    echo 'less than 30 minutes'
  fi
else
  echo "appName($myAppName) not exists in yarn application list"
  #./submit_x1_x2.sh abc TestRestartDriver #这里指定需要启动的脚本来启动相关任务
  $(nohup ./submit_checkpoint2.sh >> ./output.log 2>&1 &)
fi

监控脚本业务流程图:
监控脚本业务流程图

监控文件生成

我这里程序是spark structured streaming,因此可以注册sparkSesssion的streams()的query的监听事件

sparkSession.streams().addListener(new GlobalStreamingQueryListener(sparkSession。。。))

在监听事件中实现如下:

public class GlobalStreamingQueryListener extends StreamingQueryListener {
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(GlobalStreamingQueryListener.class);
    private static final String monitorBaseDir = "/user/dx/streaming/monitor/";
    private SparkSession sparkSession = null;
    private LongAccumulator triggerAccumulator = null;

    public GlobalStreamingQueryListener(SparkSession sparkSession, LongAccumulator triggerAccumulator) {
        this.sparkSession = sparkSession;
        this.triggerAccumulator = triggerAccumulator;
    }

    @Override
    public void onQueryStarted(QueryStartedEvent queryStarted) {
        System.out.println("Query started: " + queryStarted.id());
    }

    @Override
    public void onQueryTerminated(QueryTerminatedEvent queryTerminated) {
        System.out.println("Query terminated: " + queryTerminated.id());
    }

    @Override
    public void onQueryProgress(QueryProgressEvent queryProgress) {
        System.out.println("Query made progress: " + queryProgress.progress());
        // sparkSession.sql("select * from " +
        // queryProgress.progress().name()).show();

        triggerAccumulator.add(1);
        System.out.println("Trigger accumulator value: " + triggerAccumulator.value());

        logger.info("minitor start .... ");
        try {
            if (HDFSUtility.createDir(monitorBaseDir)) {
                logger.info("Create monitor base dir(" + monitorBaseDir + ") success");
            } else {
                logger.info("Create monitor base dir(" + monitorBaseDir + ") fail");
            }
        } catch (IOException e) {
            logger.error("An error was thrown while create monitor base dir(" + monitorBaseDir + ")");
            e.printStackTrace();
        }

        // spark.app.id application_1543820999543_0193
        String appId = this.sparkSession.conf().get("spark.app.id");
        // spark.app.name myBatchTopic
        String appName = this.sparkSession.conf().get("spark.app.name");
        String mintorFilePath = (monitorBaseDir.endsWith(File.separator) ? monitorBaseDir : monitorBaseDir + File.separator) + appName;
        logger.info("The application's id is " + appId);
        logger.info("The application's name is " + appName);
        logger.warn("If the appName is not unique,it will result in a monitor error");

        try {
            HDFSUtility.overwriter(mintorFilePath, appId + "," + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        } catch (IOException e) {
            logger.error("An error was thrown while write info to monitor file(" + mintorFilePath + ")");
            e.printStackTrace();
        }
        logger.info("minitor stop .... ");
    }

}

HDFSUtility.java中方法如下:

public class HDFSUtility {
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(HDFSUtility.class);

    /**
     * 当目录不存在时,创建目录。
     * 
     * @param dirPath
     *            目标目录
     * @return true-創建成功;false-失敗。
     * @throws IOException
     * */
    public static boolean createDir(String dirPath) throws IOException {
        FileSystem fs = null;
        Path dir = new Path(dirPath);
        boolean success = false;
        try {
            fs = FileSystem.get(new Configuration());

            if (!fs.exists(dir)) {
                success = fs.mkdirs(dir);
            } else {
                success = true;
            }
        } catch (IOException e) {
            logger.error("create dir (" + dirPath + ") fail:", e);
            throw e;
        } finally {
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return success;
    }

    /**
     * 覆盖文件写入信息
     * 
     * @param filePath
     *            目标文件路径
     * @param content
     *            被写入内容
     * @throws IOException
     * */
    public static void overwriter(String filePath, String content) throws IOException {
        FileSystem fs = null;
        // 在指定路径创建FSDataOutputStream。默认情况下会覆盖文件。
        FSDataOutputStream outputStream = null;
        Path file = new Path(filePath);

        try {
            fs = FileSystem.get(new Configuration());
            if (fs.exists(file)) {
                System.out.println("File exists(" + filePath + ")");
            }
            outputStream = fs.create(file);
            outputStream.write(content.getBytes());
        } catch (IOException e) {
            logger.error("write into file(" + filePath + ") fail:", e);
            throw e;
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

转载自:https://www.cnblogs.com/yy3b2007com/p/10241914.html

### 监控基于 Spark 和 Flink 的大数据任务 监控大数据任务是确保数据处理流程稳定、高效运行的关键环节。Spark 和 Flink 作为主流的大数据处理引擎,各自提供了丰富的监控工具和方法,以帮助开发者和运维人员实时掌握任务状态、性能瓶颈和资源使用情况。 #### Spark 任务监控工具和方法 Spark 提供了多种监控机制,包括内置的 Web UI、指标系统以及与外部监控工具的集成。 1. **Spark Web UI** Spark 自带了一个 Web UI,默认运行在 `http://<driver-node>:4040` 上,提供了任务执行的实时视图。该界面展示了作业的执行进度、任务调度信息、RDD 血缘关系、执行器状态等关键指标。 - **作业执行详情**:包括每个阶段的任务数量、执行时间、输入输出大小等。 - **Executor 信息**:显示每个 Executor 的内存使用、任务数量、GC 时间等。 - **Storage 信息**:展示缓存的 RDD 和数据块分布情况。 2. **Spark Metrics System** Spark 的 Metrics System 可以将运行时指标暴露给外部系统,支持多种输出方式,如 Console、JMX、CSV 文件、Graphite、Prometheus 等。通过配置 `spark.metrics.conf` 文件,可以灵活地定义哪些指标需要被收集和导出。 - **常用指标**:包括任务执行时间、Shuffle 读写量、Executor 内存使用、GC 时间等。 3. **与外部监控工具集成** - **Prometheus + Grafana**:通过 Prometheus 拉取 Spark 的 Metrics,使用 Grafana 进行可视化展示。 - **Ganglia**:适用于大规模集群的分布式监控系统,支持 Spark 指标收集。 - **ELK Stack(Elasticsearch + Logstash + Kibana)**:用于日志分析,帮助定位任务失败原因。 4. **Spark History Server** Spark 提供了 History Server,用于查看已完成作业的历史信息。它通过读取日志文件(通常存储在 HDFS 或本地磁盘)来重建作业执行过程,方便事后分析和调试。 5. **自定义监控脚本** 可以通过 Spark 的 REST API 获取任务状态和指标,结合自定义脚本实现自动化监控和告警。 #### Flink 任务监控工具和方法 Flink 作为一款专注于流处理的引擎,提供了更为丰富的监控功能,尤其是在实时性和状态管理方面具有优势。 1. **Flink Web UI** Flink 的 Web UI 默认运行在 `http://<jobmanager>:8081` 上,提供了任务执行的详细视图。 - **作业概览**:显示所有运行中的作业、已完成作业和失败作业的状态。 - **任务详情**:展示每个作业的 DAG 图、任务执行时间线、子任务状态、输入输出速率等。 - **Checkpoints 和 Savepoints**:展示检查点和保存点的状态、持续时间、大小等信息,帮助评估状态一致性。 2. **Flink Metrics System** Flink 支持将运行时指标暴露给多种监控系统,包括 JMX、Prometheus、Graphite、InfluxDB 等。通过配置 `metrics.reporter` 参数,可以指定使用的指标报告器。 - **常用指标**:包括任务延迟、吞吐量、反压情况、状态大小、Checkpoint 持续时间等。 3. **反压监控(Backpressure Monitoring)** Flink 提供了反压监控功能,可以检测任务中的瓶颈。通过 Web UI 或 REST API 查看各个子任务的反压状态,帮助识别数据流中的阻塞点。 4. **Checkpoint 和 Savepoint 状态监控** Flink 的 Checkpoint 机制用于保障状态一致性,可以通过 Web UI 查看 Checkpoint 的触发频率、持续时间、成功/失败次数等。Savepoint 用于手动保存状态,便于任务升级或迁移。 5. **与外部监控工具集成** - **Prometheus + Grafana**:广泛用于 Flink 的监控,支持丰富的指标可视化。 - **Alertmanager**:与 Prometheus 集成,实现自动告警。 - **ELK Stack**:用于日志分析,帮助定位任务失败原因。 6. **Flink REST API** Flink 提供了 REST API,允许开发者通过 HTTP 请求获取任务状态、指标、日志等信息,适用于自动化监控和告警系统的集成。 --- ### 示例:使用 Prometheus 和 Grafana 监控 Spark/Flink 任务 以下是一个简单的 Prometheus 配置示例,用于监控 Spark 和 Flink 任务: ```yaml scrape_configs: - job_name: 'spark' static_configs: - targets: ['spark-driver:4040'] metrics_path: '/metrics' scheme: http - job_name: 'flink' static_configs: - targets: ['flink-jobmanager:8081'] metrics_path: '/metrics' scheme: http ``` 在 Grafana 中创建仪表板,选择 Prometheus 数据源后,可以添加以下查询示例: - **Spark Executor 内存使用**: ```promql spark_executor_memory_used_bytes ``` - **Flink 任务延迟**: ```promql flink_taskmanager_task_latency ``` - **Flink Checkpoint 持续时间**: ```promql flink_jobmanager_checkpoint_duration ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值