API ProcessFunction de Flink (API de bajo nivel)

Aprendimos antes 转换算子是无法访问事件的时间戳信息和水位线信息的. Esto es extremadamente importante en algunos escenarios de aplicación. Por ejemplo, los operadores de transformación de mapas como MapFunction no pueden acceder a la marca de tiempo ni a la hora del evento actual.

En base a esto, la API de DataStream proporciona una serie de operadores de conversión de bajo nivel. Puede acceder a la marca de tiempo, marca de agua y registrar eventos cronometrados. También puede generar eventos específicos, como eventos de tiempo de espera. Process Function 用来构建事件驱动的应用以及实现自定义的业务逻辑(使用之前的window 函数和转换算子无法实现). Por ejemplo, Flink SQL se implementa mediante la función de proceso.

Flink proporciona 8 funciones de proceso:

1)ProcessFunction
(2)KeyedProcessFunction
(3)CoProcessFunction
(4)ProcessJoinFunction
(5)BroadcastProcessFunction
(6)KeyedBroadcastProcessFunction
(7)ProcessWindowFunction
(8)ProcessAllWindowFunction

一 、 KeyedProcessFunction

Aquí nos centramos en KeyedProcessFunction.

KeyedProcessFunction 用来操作 KeyedStream. KeyedProcessFunction procesará cada elemento del flujo y generará cero, uno o más elementos. Todas las funciones de proceso heredan de la interfaz RichFunction, por lo que tienen métodos como open (), close () y getRuntimeContext (). Y KeyedProcessFunction [KEY, IN, OUT] también proporciona dos métodos adicionales:

(1) processElement(v: IN, ctx: Context, out: Collector[OUT]), todos los elementos de la secuencia llamarán a este método y el resultado de la llamada se generará en el tipo de datos del recopilador. El contexto puede acceder a la marca de tiempo del elemento, la clave del elemento y el servicio de tiempo TimerService. El contexto también puede enviar los resultados a otras transmisiones (salidas laterales).

(2) onTimer(timestamp: Long, ctx: OnTimerContext, out: Collector[OUT])es una función de devolución de llamada. Se llama cuando se activa el temporizador registrado anteriormente. La marca de tiempo del parámetro es la marca de tiempo de activación establecida por el temporizador. El recopilador es una colección de resultados de salida. OnTimerContext, como el parámetro Context de processElement, proporciona cierta información sobre el contexto, como la información de tiempo (tiempo del evento o tiempo de procesamiento) desencadenado por el temporizador.

2. TimerService y Timers

El objeto TimerService mantenido por Context y OnTimerContext tiene los siguientes métodos:

(1) currentProcessingTime (): Long devuelve el tiempo de procesamiento actual

(2) currentWatermark (): Long devuelve la marca de tiempo de la marca de agua actual

(3) registerProcessingTimeTimer (marca de tiempo: Long): La unidad registrará el temporizador de tiempo de procesamiento de la clave actual. Cuando el tiempo de procesamiento llega al tiempo de temporización, active el temporizador.

(4) registerEventTimeTimer (marca de tiempo: Long): La unidad registrará el temporizador de tiempo de evento de la llave actual. Cuando la marca de agua es mayor o igual que el tiempo registrado por el temporizador, el temporizador se activa para ejecutar la función de devolución de llamada.

(5) deleteProcessingTimeTimer (marca de tiempo: Long): la unidad elimina el temporizador de tiempo de procesamiento registrado antes de eliminarlo. Si no hay un temporizador con esta marca de tiempo, no se ejecutará.

(6) deleteEventTimeTimer (marca de tiempo: larga): La unidad elimina el temporizador de tiempo de evento registrado anteriormente. Si no hay un temporizador con esta marca de tiempo, no se ejecutará.

Cuando se activa el temporizador, se ejecutará la función de devolución de llamada onTimer ().

Nota:定时器 timer 只能在keyed streams 上面使用 .

Aquí hay un ejemplo para ilustrar cómo KeyedProcessFunction opera KeyedStream.

Demanda: controle el valor de temperatura del sensor de temperatura, si el valor de temperatura aumenta continuamente dentro de un segundo (tiempo de procesamiento), se emitirá una alarma.

val warnings = readings
.keyBy(_.id)
.process(new TempIncreaseAlertFunction)

Observe cómo se implementa TempIncreaseAlertFunction. El programa usa una variable de estado como 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()
	}
}

Tres, flujo de salida lateral (SideOutput)

La salida de la mayoría de los operadores de la API DataStream 单一输出es un flujo de un determinado tipo de datos. A excepción del operador dividido, una secuencia se puede dividir en varias secuencias y los tipos de datos de estas secuencias también son los mismos. La función de salidas laterales de la función de proceso puede generar múltiples flujos, y los tipos de datos de estos flujos pueden ser diferentes. Una salida lateral se puede definir como un objeto OutputTag [X], donde X es el tipo de datos del flujo de salida. La función de proceso puede emitir un evento a una o más salidas laterales a través del objeto Context.

Aquí hay un programa de muestra:

val monitoredReadings: DataStream[SensorReading] = readings
	.process(new FreezingMonitor)
monitoredReadings
	.getSideOutput(new OutputTag[String]("freezing-alarms"))
	.print()
readings.print()

A continuación, implementamos la función FreezingMonitor para monitorear el valor de temperatura del sensor y enviar la temperatura por debajo de 32F a la salida lateral.

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

Para dos flujos de entrada, la API de DataStream proporciona operaciones de bajo nivel como CoProcessFunction. CoProcessFunction proporciona métodos para manipular cada flujo de entrada: processElement1()y processElement2().

Al igual que ProcessFunction, se utilizan ambos métodos 通过 Context 对象来调用. Este objeto Context puede acceder a datos de eventos, marca de tiempo del temporizador, TimerService y salidas laterales. CoProcessFunction también proporciona la función de devolución de llamada onTimer ().

Supongo que te gusta

Origin blog.csdn.net/weixin_43520450/article/details/108689080
Recomendado
Clasificación