Flink中解决乱序数据丢失问题Watermark(scala版)

将已经到的所有数据时间戳最大的时间再减去一个固定的延迟时间,做为关闭窗口的依据,如:某时刻已经来的数据时间最大的为5,延迟时间为2,那么该关闭的窗口就为[0,3),这里时间是从零时刻开始的。
1.无乱序数据,防止数据漏掉的方法
.assignAscendingTimestamps(_.timestamp * 1000L) // 升序数据提取时间戳 , 该方法用于没有乱序数据的情况下
2.有乱序情况下:
    a.每来一个数据生产一个设置最大延迟时间
    b.根据最大乱序来周期性生产设置最大延迟时间
1).尽可能将最大乱序程度作为assignTimestampsAndWatermarks的值,这样可以保证数据不丢失,如果设置过大会影响实时性
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[ApacheLogEvent](Time.seconds(1)) { //最大乱序程度,更加要处理的数据来查看
  override def extractTimestamp(element: ApacheLogEvent): Long = element.timestamp
}

2).通过设置窗口关闭时间来保证数据不丢失,在开窗之前设置allowedLateness

方法为:

//延迟窗口关闭时间来处理乱序数据,窗口登一分钟再关闭
.allowedLateness(Time.minutes(1))

 3).如果了为实时性和效率考虑,上述两种方法很难保证数据不丢失,就可以将迟到的数据放在测输出流中,方法为

.sideOutputLateData(new OutputTag[ApacheLogEvent]("late"))

 数据将会存放在late测输出流中,获取方法为:

val latevalue = aggStream.getSideOutput(new OutputTag[ApacheLogEvent]("late"))

 

案例:

样例类:

case class SensoreReding(id: String , timestamp: Long , temperature: Double)

核心代码:

package com.guqunyong.window


import com.guqunyong.APITest.SensoreReding
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor
import org.apache.flink.streaming.api.scala._
import org.apache.flink.streaming.api.windowing.time.Time


/**
* 处理数据乱序问题
* @author shkstart
* @create 2021-07-24 11:15
*/
object WindowTest {
  def main(args: Array[String]): Unit = {
    //创建环境
    val env = StreamExecutionEnvironment.getExecutionEnvironment
    env.setParallelism(1)
    env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
    env.getConfig.setAutoWatermarkInterval(50)
    //读取数据
    val value = env.socketTextStream("localhost", 7777)
    val dataStream = value.map(
      data => {
        val fields = data.split(",")
        SensoreReding(fields(0), fields((1)).toLong, fields(2).toDouble)
      }
    )
    //  .assignAscendingTimestamps(_.timestamp * 1000L)   //升序数据提取时间戳,该方法用于没有乱序数据的情况下
      .assignTimestampsAndWatermarks(
        new BoundedOutOfOrdernessTimestampExtractor[SensoreReding](Time.seconds(3)) {//最大乱序,即为最大延迟时间设置为3s
          override def extractTimestamp(element: SensoreReding): Long = element.timestamp * 1000L
        }
      )



    //每15秒统计一次,窗口内各传感器所有温度的最小值
    val resultStream = dataStream
      .map(data => (data.id,data.temperature,data.timestamp))
      .keyBy(_._1)
      .timeWindow(Time.seconds(15))
      .allowedLateness(Time.minutes(1))  //处理迟到1分钟的数据
      .sideOutputLateData(
        //将将漏掉的数据放到测输出流中处理
          new OutputTag[(String, Double, Long)]("late")
      )
      //.minBy(1)
      .reduce(
        (curRes,newData) => (curRes._1,curRes._2.min(newData._2),newData._3)
      )


    resultStream.print()
    //执行
    env.execute("window test")
  }
}

### Flink 任务告警方法及其实现 #### 方法一:基于日志文件的监控 通过配置 Log4j 或其他日志框架,可以设置特定的日志级别来捕获错误信息并触发告警。当检测到 ERROR 或 WARN 级别的日志条目时,可以通过邮件或其他即时通讯工具发送通知给管理员。 ```java logger.error("Task failed due to an exception", e); ``` 这种方法依赖于良好的日志记录习惯以及有效的日志解析能力[^1]。 #### 方法二:利用 Metrics API 实现性能指标监测 Apache Flink 提供了一套内置的 metrics system 来收集作业执行期间的各种统计信息。开发者可以根据这些数据设定阈值,在超过预设范围时发出警告信号。 ```java env.getConfig().disableSysoutLogging(); env.configureMetricsScopeInfo(scope, "myJob"); MetricGroup metricGroup = env.getMetricGroup(); metricGroup.gauge("customGaugeName", new Gauge<Integer>() { @Override public Integer getValue() { return someValue; } }); ``` 这种方式能够及时发现潜在问题所在,并采取相应措施加以解决[^2]。 #### 方法三:集成外部报警服务(如 Prometheus + Alertmanager) Prometheus 是一款开源的时间序列数据库,常用于微服务架构下的监控场景;而Alertmanager 则负责管理由前者产生的各类预警事件。两者结合起来可用于构建高度定制化的Flink 应用程序健康状况监视体系。 安装部署完成后只需简单几步即可完成对接: - 启动参数中加入 `metrics.reporter.prom.class=org.apache.flink.metrics.prometheus.PrometheusReporter`; - 配置好对应的 endpoint 地址以便 prometheus 可以拉取到 flink 的 metrics 数据; - 编写 alert rules 文件定义何时何地应该触发什么样的提醒动作。 上述操作均已在官方文档中有详细介绍[^3]。 #### 方法四:自定义 SourceFunction 中断逻辑 如果应用程序本身具备较强的业务特性,则可以在 source function 层面添加额外判断条件,一旦满足即刻终止整个流处理过程并向外界传达异常情况的发生。 ```scala class MySource extends RichParallelSourceFunction[String]{ override def run(ctx: SourceContext[_]): Unit ={ while (!isCanceled){ try{ val record = fetchRecord() ctx.collect(record) }catch { case ex: Exception => println(s"Error occurred during fetching records:${ex.getMessage}") cancel() } } } override def cancel():Unit={ super.cancel() } ``` 这种做法虽然直接有效但也容易造成资源浪费,因此需谨慎考虑适用场合。 #### 方法五:借助 Checkpointing 和 Savepoints 功能恢复失败节点 Checkpoint 定期保存正在运行的任务状态快照至持久化存储介质上;savepoint 类似但允许手动控制存档时机。二者均可帮助快速定位故障源头并通过重启的方式恢复正常运作模式而不丢失任何已处理过的输入项。 启用 checkpoint 设置如下所示: ```yaml execution.checkpointing.interval : 60s state.backend.type : rocksdb restart-strategy.failure-rate.max-failures-per-interval : 3 ``` 以上配置片段摘录自实际生产环境中的最佳实践案例集锦。 #### 方法六:采用 Side Output 处理旁支数据流 Side output 允许我们将不符合预期标准的数据单独分流出来做进一步分析或特殊对待。比如遇到脏数据时可以选择将其暂存在 side stream 而不是立即抛出异常中断主线程工作进度。 创建侧输出标签对象实例代码如下: ```java OutputTag<String> dirtyDataTag = new OutputTag<>("dirty-data"){}; DataStream<Tuple2<Long, String>> mainStream = ... mainStream.process(new ProcessFunction<...>(...) { private transient ListState<String> listState; @Override public void open(Configuration parameters) throws Exception { ValueStateDescriptor<String> descriptor = new ValueStateDescriptor<>(...) listState = getRuntimeContext().getListState(descriptor ); } @Override public void processElement(ProcessFunction<..., ...>.Context ctx, Collector<Tuple2<Long, String>> out) throws Exception { if (isValid(input)){ out.collect(Tuple2.of(timestamp , input)); }else{ ctx.output(dirtyDataTag,"Invalid data:" +input); } } }).getSideOutput(dirtyDataTag).print(); // 打印sideoutput的结果 ``` 这段脚本展示了如何优雅地分离正常与非正常的两条不同走向的信息传递路线图谱。 #### 方法七:运用 Watermark 控制时间窗口边界 Watermarks 表达的是事件发生的先后顺序关系而非绝对时刻点位。合理规划 watermark generator 参数有助于提高系统的容错性和灵活性,防止因乱序到达而导致误判进而影响最终决策准确性。 下面是一个简单的周期性生成固定延迟水印的例子: ```java stream.assignTimestampsAndWatermarks( new BoundedOutOfOrdernessTimestampExtractor<Event>(Time.seconds(5)) { @Override public long extractTimestamp(Event element) { return element.timestamp; }}); ``` 此处假设所有上游组件都能保证不超过指定时限内提交最新本号,否则可能引起连锁反应致使整体结构失衡紊乱不堪收拾的局面出现。 #### 方法八:结合 Tracing 工具进行全链路跟踪诊断 Distributed tracing 技术使得跨多个独立进程之间的调用链条变得清晰可见易于理解掌握全局视角下各个部分之间相互作用规律特征。Jaeger、Zipkin 等知名项目都支持同 Apache Flink 平台无缝衔接从而达成这一目标。 为了开启 Zipkin 收集器功能,需要先引入相应的 Maven 依赖库包: ```xml <dependency> <groupId>io.zipkin.brave</groupId> <artifactId>brave-instrumentation-http</artifactId> <version>${zipkin.version}</version> </dependency> <dependency> <groupId>io.zipkin.reporter2</groupId> <artifactId>zipkin-sender-okhttp3</artifactId> <version>${zipkin.sender.okhttp3.version}</version> </dependency> ``` 之后按照官方指引逐步调整应用内部接口设计使之兼容远程上报协议格式要求即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

gqy_sea

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

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

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

打赏作者

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

抵扣说明:

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

余额充值