基礎概念
支持兩種時間概念:
- Processing Time 時間遞增
- Ingestion Time : 攝入時間,數據進入Flink框架的時間,在Source Operator中設置,每個事件拿到當前時間作爲時間戳,後續的時間窗口基於該時間
- Event Time 支持一定程度的亂序
上一個 checkpoint 或者 savepoint 進行重放,是不是希望結果完全相同。如果希望結果完全相同,就只能用 Event Time;如果接受結果不同,則可以用 Processing Time。
watermark
一個watermark 代表了 watermark所包含的timestamp 數值,表示後來的數據已經再也沒有小於或等於這個時間的了.
Flink 支持兩種 watermark 生成方式:
- 在SourceFunction中產生
collectWithTimestamp 方法發送一條數據
第一個參數就是我們要發送的數據
第二個參數就是這個數據所對應的時間戳
emitWatermark 去產生一條 watermark: 表示接下來不會再有時間戳小於等於這個數值記錄
- 在使用DataStream API 的時候指定
DataStream.assignTimestampsAndWatermarks
建議生成的工作越靠近 DataSource 越好。這樣會方便讓程序邏輯裏面更多的 operator 去判斷某些數據是否亂序。
code demo:
object WaterMakerTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime)
env.setParallelism(1)
val dataStream: DataStream[Order] = env.socketTextStream("localhost", 9999).map(item => {
val itemArray = item.split(",")
Order(itemArray(0).toLong, itemArray(1), itemArray(2).toDouble)
})
val outputStream: DataStream[Order] = dataStream.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor[Order](Time.seconds(0)) {
override def extractTimestamp(element: Order): Long = element.timestamp * 1000L
}).keyBy("category").timeWindow(Time.seconds(5)).apply(new MyWindowFunction)
dataStream.print("Data")
outputStream.print("Result")
env.execute()
}
}
class MyWindowFunction extends WindowFunction[Order, Order, Tuple, TimeWindow] {
override def apply(key: Tuple, window: TimeWindow, input: Iterable[Order], out: Collector[Order]): Unit = {
val timestamp = window.maxTimestamp()
var sum: Double = 0
for (elem <- input) {
sum += elem.price
}
val category = key.asInstanceOf[Tuple1[String]].f0
out.collect(Order(timestamp, category, sum))
}
}
case class Order(timestamp: Long, category: String, price: Double)
總結
主要了解Flink的時間概念以及Watermark的作用,它可以處理亂序數據,通過watermark來定義關窗的時間點. 可以在SourceFunction和DataStream API 指定生成 Watermark.