ProcessFunction API
私たちは以前に学びました转换算子是无法访问事件的时间戳信息和水位线信息的
。これは、一部のアプリケーションシナリオでは非常に重要です。たとえば、MapFunctionなどのマップ変換演算子は、現在のイベントのタイムスタンプまたはイベント時間にアクセスできません。
これに基づいて、DataStream APIは一連の低レベル変換演算子を提供します。タイムスタンプ、透かしにアクセスし、時限イベントを登録できます。タイムアウトイベントなどの特定のイベントを出力することもできます。Process Function 用来构建事件驱动的应用以及实现自定义的业务逻辑(使用之前的window 函数和转换算子无法实现)
。たとえば、Flink SQLはプロセス関数を使用して実装されます。
Flinkは8つのプロセス関数を提供します:
(1)ProcessFunction
(2)KeyedProcessFunction
(3)CoProcessFunction
(4)ProcessJoinFunction
(5)BroadcastProcessFunction
(6)KeyedBroadcastProcessFunction
(7)ProcessWindowFunction
(8)ProcessAllWindowFunction
一、KeyedProcessFunction
ここでは、KeyedProcessFunctionに焦点を当てます。
KeyedProcessFunction 用来操作 KeyedStream
。KeyedProcessFunctionは、ストリームの各要素を処理し、ゼロ、1つ以上の要素を出力します。すべてのプロセス関数はRichFunctionインターフェイスから継承するため、open()、close()、getRuntimeContext()などのメソッドがあります。また、KeyedProcessFunction [KEY、IN、OUT]には、次の2つのメソッドも追加されています。
(1)
processElement(v: IN, ctx: Context, out: Collector[OUT])
、ストリーム内のすべての要素がこのメソッドを呼び出し、呼び出しの結果はコレクターデータ型で出力されます。コンテキストは、要素のタイムスタンプ、要素のキー、TimerServiceタイムサービスにアクセスできます。コンテキストは結果を他のストリームに出力することもできます(副出力)。
(2)
onTimer(timestamp: Long, ctx: OnTimerContext, out: Collector[OUT])
はコールバック関数です。以前に登録されたタイマーが起動すると呼び出されます。パラメータのタイムスタンプは、タイマーによって設定されるトリガーのタイムスタンプです。コレクターは、出力結果のコレクションです。OnTimerContextは、processElementのContextパラメーターと同様に、タイマーによってトリガーされる時間情報(イベント時間または処理時間)など、コンテキストに関するいくつかの情報を提供します。
2. TimerServiceとタイマー
ContextとOnTimerContextが保持するTimerServiceオブジェクトには、次のメソッドがあります。
(1)currentProcessingTime():Longは現在の処理時間を返します
(2)currentWatermark():Longは、現在の透かしのタイムスタンプを返します
(3)registerProcessingTimeTimer(timestamp:Long):ユニットは、現在のキーの処理時間タイマーを登録します。処理時間が計時時間に達したら、タイマーをトリガーします。
(4)registerEventTimeTimer(timestamp:Long):ユニットは現在のキーのイベントタイムタイマーを登録します。ウォーターマークがタイマーによって登録された時間以上の場合、タイマーがトリガーされてコールバック関数が実行されます。
(5)deleteProcessingTimeTimer(timestamp:Long):登録されている処理時間タイマーを削除してから削除します。このタイムスタンプを持つタイマーがない場合、実行されません。
(6)deleteEventTimeTimer(timestamp:Long):以前に登録されたイベントタイムタイマーを削除し、このタイムスタンプを持つタイマーがない場合は実行されません。
タイマーがトリガーされると、コールバック関数onTimer()が実行されます。
注:
定时器 timer 只能在keyed streams 上面使用
。
以下は、KeyedProcessFunctionがKeyedStreamを操作する方法を示す例です。
要求:温度センサーの温度値を監視します。温度値が1秒(処理時間)以内に連続して上昇すると、アラームが発行されます。
val warnings = readings
.keyBy(_.id)
.process(new TempIncreaseAlertFunction)
TempIncreaseAlertFunctionがどのように実装されているかを見てみましょう。プログラムはValueStateのような状態変数を使用します。
class TempIncreaseAlertFunction extends KeyedProcessFunction[String, SensorReading, String] {
// 保存上一个传感器温度值
lazy val lastTemp: ValueState[Double] = getRuntimeContext.getState(
new ValueStateDescriptor[Double]("lastTemp", Types.of[Double])
)
// 保存注册的定时器的时间戳
lazy val currentTimer: ValueState[Long] = getRuntimeContext.getState(
new ValueStateDescriptor[Long]("timer", Types.of[Long])
)
override def processElement(r: SensorReading, ctx: KeyedProcessFunction[String, SensorReading, String]#Context, out: Collector[String]): Unit = {
// 取出上一次的温度
val prevTemp = lastTemp.value()
// 将当前温度更新到上一次的温度这个变量中
lastTemp.update(r.temperature)
val curTimerTimestamp = currentTimer.value()
if (prevTemp == 0.0 || r.temperature < prevTemp) {
// 温度下降或者是第一个温度值,删除定时器
ctx.timerService().deleteProcessingTimeTimer(curTimerTimestamp)
// 清空状态变量
currentTimer.clear()
} else if (r.temperature > prevTemp && curTimerTimestamp == 0) {
// 温度上升且我们并没有设置定时器
val timerTs = ctx.timerService().currentProcessingTime() + 1000
ctx.timerService().registerProcessingTimeTimer(timerTs)
currentTimer.update(timerTs)
}
}
override def onTimer(ts: Long, ctx: KeyedProcessFunction[String, SensorReading, String]#OnTimerContext, out: Collector[String]): Unit = {
out.collect("传感器 id 为: " + ctx.getCurrentKey + "的传感器温度值已经连续 1s 上升了。")
currentTimer.clear()
}
}
3つの副出力ストリーム(SideOutput)
DataStream APIのほとんどの演算子の出力は单一输出
、特定のデータ型のストリームです。split演算子を除いて、1つのストリームは複数のストリームに分割でき、これらのストリームのデータ型も同じです。processfunctionの副出力関数は複数のストリームを生成でき、これらのストリームのデータ型は異なる場合があります。副出力は、OutputTag [X]オブジェクトとして定義できます。ここで、Xは出力ストリームのデータ型です。プロセス関数は、Contextオブジェクトを通じて1つ以上の副出力にイベントを発行できます。
ここにサンプルプログラムがあります:
val monitoredReadings: DataStream[SensorReading] = readings
.process(new FreezingMonitor)
monitoredReadings
.getSideOutput(new OutputTag[String]("freezing-alarms"))
.print()
readings.print()
次に、FreezingMonitor関数を実装して、センサーの温度値を監視し、32F未満の温度をサイド出力に出力します。
class FreezingMonitor extends ProcessFunction[SensorReading, SensorReading] {
// 定义一个侧输出标签
lazy val freezingAlarmOutput: OutputTag[String] = new OutputTag[String]("freezing-alarms")
override def processElement(r: SensorReading, ctx: ProcessFunction[SensorReading, SensorReading]#Context, out: Collector[SensorReading]): Unit = {
// 温度在 32F 以下时,输出警告信息
if (r.temperature < 32.0) {
ctx.output(freezingAlarmOutput, s"Freezing Alarm for ${r.id}")
}
// 所有数据直接常规输出到主流
out.collect(r)
}
}
四、CoProcessFunction
2つの入力ストリームに対して、DataStream APIはCoProcessFunctionなどの低レベルの操作を提供します。CoProcessFunctionは、各入力ストリームを操作するための方法を提供する:processElement1()
およびprocessElement2()
。
ProcessFunctionと同様に、両方のメソッドが使用され通过 Context 对象来调用
ます。このContextオブジェクトは、イベントデータ、タイマータイムスタンプ、TimerService、および副出力にアクセスできます。CoProcessFunctionは、onTimer()コールバック関数も提供します。