StreamPark获取Flink中信息的两种方式源码解读

StreamPark获取Flink中信息的两种方式源码解读

​ 经过对SP的源码解读,发现SP对Flink的Metric等信息的监控分为两种实现方式,在K8S模式下获取的方式和其他方式有所区别,且K8S模式下为scala代码实现,其他模式为java代码实现,不过最终都是从Flink Web UI中获取数据。

一、当作业启动在yarn、独立、远程模式下时,SP获取flink信息

1、该模式下对Flink监控的入口类为FlinkRESTAPIWatcher.java,通过start方法作为监控Flink信息的入口,由代码分析可以得知,在以下两种条件下会执行。

(1)当程序启动或页面操作的任务,如启动/停止,需要立即返回状态。(频率1秒一次,连续10秒(10次))

(2)正常信息获取,每5秒获取一次

  @Scheduled(fixedDelay = 1000)
  public void start() {
    // The application has been started at the first time, or the front-end is operating start/stop,
    // need to return status info immediately.
    if (lastWatchingTime == null || !OPTIONING.isEmpty()) {
      doWatch();
    } else if (System.currentTimeMillis() - lastOptionTime <= OPTION_INTERVAL) {
      // The last operation time is less than option interval.(10 seconds)
      doWatch();
    } else if (System.currentTimeMillis() - lastWatchingTime >= WATCHING_INTERVAL) {
      // Normal information obtain, check if there is 5 seconds interval between this time and the
      // last time.(once every 5 seconds)
      doWatch();
    }
  }

2、接下来是doWatch方法,该方法主要是通过线程池执行监控任务,可以看到这一段代码中主要获取状态的方法分为getFromFlinkRestApigetFromYarnRestApi,该程序以getFromFlinkRestApi为主,当通过这个方法获取失败时,会通过getFromYarnRestApi方法进行获取。

  private void doWatch() {
    lastWatchingTime = System.currentTimeMillis();
    for (Map.Entry<Long, Application> entry : WATCHING_APPS.entrySet()) {
      EXECUTOR.execute(
          () -> {
            long key = entry.getKey();
            Application application = entry.getValue();
            final StopFrom stopFrom =
                STOP_FROM_MAP.getOrDefault(key, null) == null
                    ? StopFrom.NONE
                    : STOP_FROM_MAP.get(key);
            final OptionState optionState = OPTIONING.get(key);
            try {
              // query status from flink rest api
              Utils.required(application.getId() != null);
              getFromFlinkRestApi(application, stopFrom);
            } catch (Exception flinkException) {
              // query status from yarn rest api
              try {
                getFromYarnRestApi(application, stopFrom);
              } catch (Exception yarnException) {
                /*
                 Query from flink's restAPI and yarn's restAPI both failed.
                 In this case, it is necessary to decide whether to return to the final state depending on the state being operated
                */
                if (optionState == null || !optionState.equals(OptionState.STARTING)) {
                  // non-mapping
                  if (application.getState() != FlinkAppState.MAPPING.getValue()) {
                    log.error(
                        "FlinkRESTAPIWatcher getFromFlinkRestApi and getFromYarnRestApi error,job failed,savePoint expired!");
                    if (StopFrom.NONE.equals(stopFrom)) {
                      savePointService.expire(application.getId());
                      application.setState(FlinkAppState.LOST.getValue());
                      alertService.alert(application, FlinkAppState.LOST);
                    } else {
                      application.setState(FlinkAppState.CANCELED.getValue());
                    }
                  }
                  /*
                   This step means that the above two ways to get information have failed, and this step is the last step,
                   which will directly identify the mission as cancelled or lost.
                   Need clean savepoint.
                  */
                  application.setEndTime(new Date());
                  cleanSavepoint(application);
                  cleanOptioning(optionState, key);
                  doPersistMetrics(application, true);
                  FlinkAppState appState = FlinkAppState.of(application.getState());
                  if (appState.equals(FlinkAppState.FAILED)
                      || appState.equals(FlinkAppState.LOST)) {
                    alertService.alert(application, FlinkAppState.of(application.getState()));
                    if (appState.equals(FlinkAppState.FAILED)) {
                      try {
                        applicationService.start(application, true);
                      } catch (Exception e) {
                        log.error(e.getMessage(), e);
                      }
                    }
                  }
                }
              }
            }
          });
    }
  }

3、通过分析getFromFlinkRestApi方法得知,该方法主要调用的方法为httpJobsOverviewhttpCheckpoints,通过这两个方法来获取Flink信息

private void getFromFlinkRestApi(Application application, StopFrom stopFrom) throws Exception {
    FlinkCluster flinkCluster = getFlinkCluster(application);
    JobsOverview jobsOverview = httpJobsOverview(application, flinkCluster);
    Optional<JobsOverview.Job> optional;
    ExecutionMode execMode = application.getExecutionModeEnum();
    if (ExecutionMode.YARN_APPLICATION.equals(execMode)
        || ExecutionMode.YARN_PER_JOB.equals(execMode)) {
      optional =
          jobsOverview.getJobs().size() > 1
              ? jobsOverview.getJobs().stream()
                  .filter(a -> StringUtils.equals(application.getJobId(), a.getId()))
                  .findFirst()
              : jobsOverview.getJobs().stream().findFirst();
    } else {
      optional =
          jobsOverview.getJobs().stream()
              .filter(x -> x.getId().equals(application.getJobId()))
              .findFirst();
    }
    if (optional.isPresent()) {

      JobsOverview.Job jobOverview = optional.get();
      FlinkAppState currentState = FlinkAppState.of(jobOverview.getState());

      if (!FlinkAppState.OTHER.equals(currentState)) {
        try {
          // 1) set info from JobOverview
          handleJobOverview(application, jobOverview);
        } catch (Exception e) {
          log.error("get flink jobOverview error: {}", e.getMessage(), e);
        }
        try {
          // 2) CheckPoints
          handleCheckPoints(application);
        } catch (Exception e) {
          log.error("get flink jobOverview error: {}", e.getMessage(), e);
        }
        // 3) savePoint obsolete check and NEED_START check
        OptionState optionState = OPTIONING.get(application.getId());
        if (currentState.equals(FlinkAppState.RUNNING)) {
          handleRunningState(application, optionState, currentState);
        } else {
          handleNotRunState(application, optionState, currentState, stopFrom);
        }
      }
    }
  }

4、通过分析httpOverviewhttpJobsOverviewhttpCheckpoints方法,得知SP是通过调用Flink的WebUI界面的http请求,来获取Flink相关信息。

private Overview httpOverview(Application application, FlinkCluster flinkCluster)
      throws IOException {
    String appId = application.getAppId();
    if (appId != null) {
      if (application.getExecutionModeEnum().equals(ExecutionMode.YARN_APPLICATION)
          || application.getExecutionModeEnum().equals(ExecutionMode.YARN_PER_JOB)) {
        String reqURL;
        if (StringUtils.isEmpty(application.getJobManagerUrl())) {
          String format = "proxy/%s/overview";
          reqURL = String.format(format, appId);
        } else {
          String format = "%s/overview";
          reqURL = String.format(format, application.getJobManagerUrl());
        }
        return yarnRestRequest(reqURL, Overview.class);
      }
    }
    return null;
  }

  private JobsOverview httpJobsOverview(Application application, FlinkCluster flinkCluster)
      throws Exception {
    final String flinkUrl = "jobs/overview";
    ExecutionMode execMode = application.getExecutionModeEnum();
    if (ExecutionMode.YARN_PER_JOB.equals(execMode)
        || ExecutionMode.YARN_APPLICATION.equals(execMode)) {
      String reqURL;
      if (StringUtils.isEmpty(application.getJobManagerUrl())) {
        String format = "proxy/%s/" + flinkUrl;
        reqURL = String.format(format, application.getAppId());
      } else {
        String format = "%s/" + flinkUrl;
        reqURL = String.format(format, application.getJobManagerUrl());
      }
      return yarnRestRequest(reqURL, JobsOverview.class);
    } else if (ExecutionMode.REMOTE.equals(execMode)
        || ExecutionMode.YARN_SESSION.equals(execMode)) {
      if (application.getJobId() != null) {
        String remoteUrl = flinkCluster.getAddress() + "/" + flinkUrl;
        JobsOverview jobsOverview = httpRestRequest(remoteUrl, JobsOverview.class);
        if (jobsOverview != null) {
          List<JobsOverview.Job> jobs =
              jobsOverview.getJobs().stream()
                  .filter(x -> x.getId().equals(application.getJobId()))
                  .collect(Collectors.toList());
          jobsOverview.setJobs(jobs);
        }
        return jobsOverview;
      }
    }
    return null;
  }

  private CheckPoints httpCheckpoints(Application application, FlinkCluster flinkCluster)
      throws IOException {
    final String flinkUrl = "jobs/%s/checkpoints";
    ExecutionMode execMode = application.getExecutionModeEnum();
    if (ExecutionMode.YARN_PER_JOB.equals(execMode)
        || ExecutionMode.YARN_APPLICATION.equals(execMode)) {
      String reqURL;
      if (StringUtils.isEmpty(application.getJobManagerUrl())) {
        String format = "proxy/%s/" + flinkUrl;
        reqURL = String.format(format, application.getAppId(), application.getJobId());
      } else {
        String format = "%s/" + flinkUrl;
        reqURL = String.format(format, application.getJobManagerUrl(), application.getJobId());
      }
      return yarnRestRequest(reqURL, CheckPoints.class);
    } else if (ExecutionMode.REMOTE.equals(execMode)
        || ExecutionMode.YARN_SESSION.equals(execMode)) {
      if (application.getJobId() != null) {
        String remoteUrl =
            flinkCluster.getAddress() + "/" + String.format(flinkUrl, application.getJobId());
        return httpRestRequest(remoteUrl, CheckPoints.class);
      }
    }
    return null;
  }

二、当作业启动在K8S模式下时,SP获取flink信息

1、该模式下对Flink监控的入口类为FlinkK8sWatcherWrapper.java,通过registerFlinkK8sWatcher方法作为监控Flink信息的入口,由代码分析可以得知,该方法主要做的是加载配置文件、加载监控类。

  @Bean(destroyMethod = "close")
  public FlinkK8sWatcher registerFlinkK8sWatcher() {
    // lazy start tracking monitor
    FlinkK8sWatcher flinkK8sWatcher =
        FlinkK8sWatcherFactory.createInstance(FlinkTrackConfig.fromConfigHub(), true);
    initFlinkK8sWatcher(flinkK8sWatcher);

    /* Dev scaffold: watch flink k8s tracking cache,
       see org.apache.streampark.flink.kubernetes.helper.KubernetesWatcherHelper for items.
       Example:
           KubernetesWatcherHelper.watchTrackIdsCache(flinkK8sWatcher);
           KubernetesWatcherHelper.watchJobStatusCache(flinkK8sWatcher);
           KubernetesWatcherHelper.watchAggClusterMetricsCache(flinkK8sWatcher);
           KubernetesWatcherHelper.watchClusterMetricsCache(flinkK8sWatcher);
    */
    return flinkK8sWatcher;
  }

2、通过分析TrackConfig.scala中的**FlinkTrackConfig.fromConfigHub()**方法得知,获取作业状态默认模式下为每5秒获取一次,获取集群Metric信息为每10秒获取一次,超时时间为120秒

object JobStatusWatcherConfig {
  def defaultConf: JobStatusWatcherConfig = JobStatusWatcherConfig(
    requestTimeoutSec = 120,
    requestIntervalSec = 5,
    silentStateJobKeepTrackingSec = 60)

  def debugConf: JobStatusWatcherConfig = JobStatusWatcherConfig(
    requestTimeoutSec = 120,
    requestIntervalSec = 2,
    silentStateJobKeepTrackingSec = 5)
}

object MetricWatcherConfig {
  def defaultConf: MetricWatcherConfig = MetricWatcherConfig(
    requestTimeoutSec = 120,
    requestIntervalSec = 10)

  def debugConf: MetricWatcherConfig = MetricWatcherConfig(
    requestTimeoutSec = 120,
    requestIntervalSec = 2)
}

3、通过分析initFlinkK8sWatcher方法得知,注册了一个名为FlinkK8sChangeEventListener监听器,将此监听器注册到了eventBus

  private void initFlinkK8sWatcher(@Nonnull FlinkK8sWatcher trackMonitor) {
    // register change event listener
    trackMonitor.registerListener(flinkK8sChangeEventListener);
    // recovery tracking list
    List<TrackId> k8sApp = getK8sWatchingApps();
    k8sApp.forEach(trackMonitor::doWatching);
  }

4、该监听器的主要作用是捕获FlinkJobStatusChangeEvent,然后将其永久存储到数据库中。实际更新

@Subscribe
  public void subscribeJobStatusChange(FlinkJobStatusChangeEvent event) {
    JobStatusCV jobStatus = event.jobStatus();
    TrackId trackId = event.trackId();
    // get pre application record
    Application app = applicationService.getById(trackId.appId());
    if (app == null) {
      return;
    }
    // update application record
    setByJobStatusCV(app, jobStatus);
    applicationService.persistMetrics(app);

    // email alerts when necessary
    FlinkAppState state = FlinkAppState.of(app.getState());
    if (FlinkAppState.FAILED.equals(state)
        || FlinkAppState.LOST.equals(state)
        || FlinkAppState.RESTARTING.equals(state)
        || FlinkAppState.FINISHED.equals(state)) {
      IngressController.deleteIngress(app.getClusterId(), app.getK8sNamespace());
      executor.execute(() -> alertService.alert(app, state));
    }
  }

5、通过查看DefaultFlinkK8sWatcher.scala源码得知,其中创建了用于存储跟踪结果的缓存池、用于更改事件的eventBus、Flink几种指标监视器的实现类FlinkK8sEventWatcher.scala、FlinkJobStatusWatcher.scala、FlinkMetricWatcher.scala、FlinkCheckpointWatcher.scala

class DefaultFlinkK8sWatcher(conf: FlinkTrackConfig = FlinkTrackConfig.defaultConf) extends FlinkK8sWatcher {

  // cache pool for storage tracking result
  implicit val watchController: FlinkK8sWatchController = new FlinkK8sWatchController()

  // eventBus for change event
  implicit lazy val eventBus: ChangeEventBus = {
    val eventBus = new ChangeEventBus()
    eventBus.registerListener(new BuildInEventListener)
    eventBus
  }

  // remote server tracking watcher
  val k8sEventWatcher = new FlinkK8sEventWatcher()
  val jobStatusWatcher = new FlinkJobStatusWatcher(conf.jobStatusWatcherConf)
  val metricsWatcher = new FlinkMetricWatcher(conf.metricWatcherConf)
  val checkpointWatcher = new FlinkCheckpointWatcher(conf.metricWatcherConf)

  private[this] val allWatchers = Array[FlinkWatcher](k8sEventWatcher, jobStatusWatcher, metricsWatcher, checkpointWatcher)

6、通过查看FlinkJobStatusWatcher.scala源码可以得知,该模式也是通过Flink的WEB UI界面的接口获取FLink的相关信息,只不过实现方式不同

  private def callJobsOverviewsApi(restUrl: String): Option[JobDetails] = {
    val jobDetails = JobDetails.as(
      Request.get(s"$restUrl/jobs/overview")
        .connectTimeout(Timeout.ofSeconds(KubernetesRetriever.FLINK_REST_AWAIT_TIMEOUT_SEC))
        .responseTimeout(Timeout.ofSeconds(KubernetesRetriever.FLINK_CLIENT_TIMEOUT_SEC))
        .execute.returnContent().asString(StandardCharsets.UTF_8))
    jobDetails
  }

private[kubernetes] object JobDetails {

  @transient
  implicit lazy val formats: DefaultFormats.type = org.json4s.DefaultFormats

  def as(json: String): Option[JobDetails] = {

    Try(parse(json)) match {
      case Success(ok) =>
        ok \ "jobs" match {
          case JNothing | JNull => None
          case JArray(arr) =>
            val details = arr.map(x => {
              val task = x \ "tasks"
              JobDetail(
                (x \ "jid").extractOpt[String].orNull,
                (x \ "name").extractOpt[String].orNull,
                (x \ "state").extractOpt[String].orNull,
                (x \ "start-time").extractOpt[Long].getOrElse(0),
                (x \ "end-time").extractOpt[Long].getOrElse(0),
                (x \ "duration").extractOpt[Long].getOrElse(0),
                (x \ "last-modification").extractOpt[Long].getOrElse(0),
                JobTask(
                  (task \ "total").extractOpt[Int].getOrElse(0),
                  (task \ "created").extractOpt[Int].getOrElse(0),
                  (task \ "scheduled").extractOpt[Int].getOrElse(0),
                  (task \ "deploying").extractOpt[Int].getOrElse(0),
                  (task \ "running").extractOpt[Int].getOrElse(0),
                  (task \ "finished").extractOpt[Int].getOrElse(0),
                  (task \ "canceling").extractOpt[Int].getOrElse(0),
                  (task \ "canceled").extractOpt[Int].getOrElse(0),
                  (task \ "failed").extractOpt[Int].getOrElse(0),
                  (task \ "reconciling").extractOpt[Int].getOrElse(0),
                  (task \ "initializing").extractOpt[Int].getOrElse(0)))
            }).toArray
            Some(JobDetails(details))
          case _ => None
        }
      case Failure(_) => None
    }

  }

}

三、两种模式下获取到的Flink信息

1、通过分析代码得知,可获取到的信息有

(1)集群信息:

 {
    "taskmanagers":1,
    "slots-total":3,
    "slots-available":2,
    "jobs-running":1,
    "jobs-finished":0,
    "jobs-cancelled":0,
    "jobs-failed":0,
    "flink-version":"1.16.0",
    "flink-commit":"af6eff8"
}

(2)作业信息:

{
    "jobs":[
        {
            "jid":"34cfd6635b35505f9365460f8920e551",
            "name":"Flink Streaming Job",
            "state":"RUNNING",
            "start-time":1682231940127,
            "end-time":-1,
            "duration":143155,
            "last-modification":1682231940664,
            "tasks":{
                "total":2,
                "created":0,
                "scheduled":0,
                "deploying":0,
                "running":2,
                "finished":0,
                "canceling":0,
                "canceled":0,
                "failed":0,
                "reconciling":0,
                "initializing":0
            }
        }
    ]
}

(3)checkpoint信息:

{
    "counts":{
        "restored":0,
        "total":13,
        "in_progress":0,
        "completed":13,
        "failed":0
    },
    "summary":{
        "checkpointed_size":{
            "min":1878,
            "max":1878,
            "avg":1878,
            "p50":1878,
            "p90":1878,
            "p95":1878,
            "p99":1878,
            "p999":1878
        },
        "state_size":{
            "min":1878,
            "max":1878,
            "avg":1878,
            "p50":1878,
            "p90":1878,
            "p95":1878,
            "p99":1878,
            "p999":1878
        },
        "end_to_end_duration":{
            "min":11,
            "max":79,
            "avg":21,
            "p50":18,
            "p90":55.79999999999998,
            "p95":79,
            "p99":79,
            "p999":79
        },
        "alignment_buffered":{
            "min":0,
            "max":0,
            "avg":0,
            "p50":0,
            "p90":0,
            "p95":0,
            "p99":0,
            "p999":0
        },
        "processed_data":{
            "min":0,
            "max":0,
            "avg":0,
            "p50":0,
            "p90":0,
            "p95":0,
            "p99":0,
            "p999":0
        },
        "persisted_data":{
            "min":0,
            "max":0,
            "avg":0,
            "p50":0,
            "p90":0,
            "p95":0,
            "p99":0,
            "p999":0
        }
    },
    "latest":{
        "completed":{
            "className":"completed",
            "id":13,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232181234,
            "latest_ack_timestamp":1682232181246,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":12,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-13",
            "discarded":false
        },
        "savepoint":null,
        "failed":null,
        "restored":null
    },
    "history":[
        {
            "className":"completed",
            "id":13,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232181234,
            "latest_ack_timestamp":1682232181246,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":12,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-13",
            "discarded":false
        },
        {
            "className":"completed",
            "id":12,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232161230,
            "latest_ack_timestamp":1682232161243,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":13,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-12",
            "discarded":true
        },
        {
            "className":"completed",
            "id":11,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232141233,
            "latest_ack_timestamp":1682232141247,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":14,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-11",
            "discarded":true
        },
        {
            "className":"completed",
            "id":10,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232121237,
            "latest_ack_timestamp":1682232121255,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":18,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-10",
            "discarded":true
        },
        {
            "className":"completed",
            "id":9,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232101236,
            "latest_ack_timestamp":1682232101247,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":11,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-9",
            "discarded":true
        },
        {
            "className":"completed",
            "id":8,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232081238,
            "latest_ack_timestamp":1682232081254,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":16,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-8",
            "discarded":true
        },
        {
            "className":"completed",
            "id":7,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232061239,
            "latest_ack_timestamp":1682232061257,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":18,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-7",
            "discarded":true
        },
        {
            "className":"completed",
            "id":6,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232041240,
            "latest_ack_timestamp":1682232041259,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":19,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-6",
            "discarded":true
        },
        {
            "className":"completed",
            "id":5,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232021241,
            "latest_ack_timestamp":1682232021260,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":19,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-5",
            "discarded":true
        },
        {
            "className":"completed",
            "id":4,
            "status":"COMPLETED",
            "is_savepoint":false,
            "trigger_timestamp":1682232001238,
            "latest_ack_timestamp":1682232001257,
            "checkpointed_size":1878,
            "state_size":1878,
            "end_to_end_duration":19,
            "alignment_buffered":0,
            "processed_data":0,
            "persisted_data":0,
            "num_subtasks":2,
            "num_acknowledged_subtasks":2,
            "checkpoint_type":"CHECKPOINT",
            "tasks":{

            },
            "external_path":"file:/Users/wangkeshuai/Documents/checkpoint/34cfd6635b35505f9365460f8920e551/chk-4",
            "discarded":true
        }
    ]
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值