Función de ventana de Windows (agregación incremental)

El caso puede hacer referencia a (cambie a la rama Flink1.12 para ver el código más reciente): https://github.com/perkinls/flink-local-train

Después de definir el asignador de ventanas, necesitamos especificar los cálculos que se realizarán en cada ventana. Esta es la responsabilidad de la función de ventana, una vez que el sistema determina que una ventana está lista para ser procesada, se puede utilizar para procesar los elementos de cada ventana (agrupados por clave).
expediente
ProcessWindowFunction se puede combinar con ReduceFunction o AggregateFunction para agregarlos de forma incremental cuando llegan a la ventana. Cuando se cierra la ventana, ProcessWindowFunction proporcionará el resultado de la agregación. Hace que ProcessWindowFunction calcule la ventana de forma incremental mientras puede obtener la metainformación de la ventana.

Reducir Función

ReduceFunction 增量聚合combina dos elementos en la entrada para producir el mismo tipo de elemento de salida.

val input: DataStream[(String, Long)] = ...

// 汇总了窗口中所有元素的元组的第二个字段
input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .reduce {
    
     (v1, v2) => (v1._1, v1._2 + v2._2) }

Función agregada

AggregateFunction es una versión general de ReduceFunction, con tres tipos: tipo de entrada (IN), tipo de acumulador (ACC) y tipo de salida (OUT). AggregateFunction tiene un método para agregar un elemento de entrada al acumulador. La interfaz también tiene métodos para crear un acumulador inicial, fusionar dos acumuladores en un acumulador y extraer la salida (tipo OUT) del acumulador.

Al igual que ReduceFunction, Flink realizará los elementos de entrada de la ventana cuando lleguen 增量聚合.

/**
 * 累加器用于保存一个运行求和与一个计数。[getResult]方法计算平均值。
 * 计算窗口中元素的第二个字段的平均值
 */
class AverageAggregate extends AggregateFunction[(String, Long), (Long, Long), Double] {
    
    
  override def createAccumulator() = (0L, 0L)

  override def add(value: (String, Long), accumulator: (Long, Long)) =
    (accumulator._1 + value._2, accumulator._2 + 1L)

  override def getResult(accumulator: (Long, Long)) = accumulator._1 / accumulator._2

  override def merge(a: (Long, Long), b: (Long, Long)) =
    (a._1 + b._1, a._2 + b._2)
}

val input: DataStream[(String, Long)] = ...

input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .aggregate(new AverageAggregate)

ProcessWindowFunction

ProcessWindowFunction obtiene un Iterable, que contiene todos los elementos de la ventana, y un objeto Context, que puede acceder a información de tiempo y estado, haciéndolo más flexible que otras funciones de ventana. Esto tiene un costo de rendimiento y consumo de recursos, porque los elementos no se pueden agregar de forma incremental, sino que deben almacenarse en búfer internamente hasta que se considere que la ventana está lista para su procesamiento.

abstract class ProcessWindowFunction[IN, OUT, KEY, W <: Window] extends Function {
    
    

  /**
    * Evaluates the window and outputs none or several elements.
    *
    * @param key      The key for which this window is evaluated.
    * @param context  The context in which the window is being evaluated.
    * @param elements The elements in the window being evaluated.
    * @param out      A collector for emitting elements.
    * @throws Exception The function may throw exceptions to fail the program and trigger recovery.
    */
  def process(
      key: KEY,
      context: Context,
      elements: Iterable[IN],
      out: Collector[OUT])

  /**
    * The context holding window metadata
    */
  abstract class Context {
    
    
    /**
      * Returns the window that is being evaluated.
      */
    def window: W

    /**
      * Returns the current processing time.
      */
    def currentProcessingTime: Long

    /**
      * Returns the current event-time watermark.
      */
    def currentWatermark: Long

    /**
      * State accessor for per-key and per-window state.
      */
    def windowState: KeyedStateStore

    /**
      * State accessor for per-key global state.
      */
    def globalState: KeyedStateStore
  }

}

Puede definir y usar ProcessWindowFunction así:

val input: DataStream[(String, Long)] = ...

input
  .keyBy(_._1)
  .timeWindow(Time.minutes(5))
  .process(new MyProcessWindowFunction())

/* 窗口中的元素进行计数 */

class MyProcessWindowFunction extends ProcessWindowFunction[(String, Long), String, String, TimeWindow] {
    
    

  def process(key: String, context: Context, input: Iterable[(String, Long)], out: Collector[String]): () = {
    
    
    var count = 0L
    for (in <- input) {
    
    
      count = count + 1
    }
    out.collect(s"Window ${context.window} count: $count")
  }
}

ProcessWindowFunction agregación incremental

Puede combinar ProcessWindowFunction con ReduceFunction y AggregateFunction para agregar elementos de forma incremental cuando llegan a la ventana. Cuando se cierra la ventana, el resultado agregado se proporcionará a ProcessWindowFunction. Esto hace posible calcular la ventana de forma incremental y al mismo tiempo acceder a otra metainformación de ventana de ProcessWindowFunction.

También puede utilizar la función WindowFunction tradicional en lugar de ProcessWindowFunction para agregar ventanas de forma incremental.

Agregación incremental de ventanas con función Reducir

El siguiente ejemplo muestra cómo combinar la función ReduceFunction incremental con ProcessWindowFunction para devolver el evento más pequeño en la ventana y la hora de inicio de la ventana.

val input: DataStream[SensorReading] = ...

input
  .keyBy(<key selector>)
  .timeWindow(<duration>)
  .reduce(
    (r1: SensorReading, r2: SensorReading) => {
    
     if (r1.value > r2.value) r2 else r1 },
    ( key: String,
      context: ProcessWindowFunction[_, _, _, TimeWindow]#Context,
      minReadings: Iterable[SensorReading],
      out: Collector[(Long, SensorReading)] ) =>
      {
    
    
        val min = minReadings.iterator.next()
        out.collect((context.window.getStart, min))
      }
  )

Agregación de ventana incremental con AggregateFunction

El siguiente ejemplo muestra cómo combinar AggregateFunction incremental con ProcessWindowFunction para calcular el promedio y emitir la clave y la ventana junto con el promedio.

val input: DataStream[(String, Long)] = ...

input
  .keyBy(<key selector>)
  .timeWindow(<duration>)
  .aggregate(new AverageAggregate(), new MyProcessWindowFunction())

// Function definitions

/**
 * The accumulator is used to keep a running sum and a count. The [getResult] method
 * computes the average.
 */
class AverageAggregate extends AggregateFunction[(String, Long), (Long, Long), Double] {
    
    
  override def createAccumulator() = (0L, 0L)

  override def add(value: (String, Long), accumulator: (Long, Long)) =
    (accumulator._1 + value._2, accumulator._2 + 1L)

  override def getResult(accumulator: (Long, Long)) = accumulator._1 / accumulator._2

  override def merge(a: (Long, Long), b: (Long, Long)) =
    (a._1 + b._1, a._2 + b._2)
}

class MyProcessWindowFunction extends ProcessWindowFunction[Double, (String, Double), String, TimeWindow] {
    
    

  def process(key: String, context: Context, averages: Iterable[Double], out: Collector[(String, Double)]): () = {
    
    
    val average = averages.iterator.next()
    out.collect((key, average))
  }
}

ProcessWindowFunction obtiene el estado de cada ventana

Además de acceder al estado de la clave (cualquier función Rich está bien), ProcessWindowFunction también puede usar el estado de la clave de la ventana que la función está procesando actualmente. En este caso, es importante comprender a qué se refiere cada estado de ventana. Involucrando diferentes "ventanas":

  • Especifique la ventana definida durante el funcionamiento de la ventana: puede ser una ventana móvil de 1 hora o una ventana deslizante de 2 horas que se desliza durante 1 hora.
  • Un ejemplo práctico de una ventana definida por una clave dada: para el ID de usuario xyz, esta puede ser la ventana de tiempo de 12:00 a 13:00. Esto se basa en la definición de la ventana, y dependiendo de la cantidad de claves que el trabajo está procesando actualmente y del intervalo de tiempo al que pertenece el evento, habrá muchas ventanas.

El estado de cada ventana está relacionado con el último de los dos. Esto significa que si tratamos con 1000 eventos con diferentes claves, y todos los eventos actuales pertenecen a la ventana de tiempo [12:00, 13:00), entonces habrá 1000 instancias de ventana, cada una con su propio estado de Ventana.

Hay dos formas de llamar a process () en el objeto Context para acceder a dos estados:

  • globalState (), que permite el acceso a estados clave que no están dentro del alcance de la ventana
  • windowState (), que permite el acceso al estado con clave que también actúa en la ventana

Esta función es útil si espera que la misma ventana se active varias veces, por ejemplo, si tiene un activador anterior para datos tardíos o si tiene un activador personalizado que se activa especulativamente antes. En este caso, almacenará información sobre el desencadenante anterior o el número de desencadenantes en cada estado de ventana.

Al utilizar el estado de la ventana, es importante utilizar el método clear () para borrar el estado al borrar la ventana.

WindowFunction versiones antiguas

En algunos lugares donde se puede usar ProcessWindowFunction, también puede usar WindowFunction. Esta es una versión anterior de ProcessWindowFunction, que proporciona menos información contextual y no tiene algunas funciones avanzadas, como el estado de la clave de cada ventana. Esta interfaz quedará obsoleta en algún momento.

trait WindowFunction[IN, OUT, KEY, W <: Window] extends Function with Serializable {
    
    

  /**
    * Evaluates the window and outputs none or several elements.
    *
    * @param key    The key for which this window is evaluated.
    * @param window The window that is being evaluated.
    * @param input  The elements in the window being evaluated.
    * @param out    A collector for emitting elements.
    * @throws Exception The function may throw exceptions to fail the program and trigger recovery.
    */
  def apply(key: KEY, window: W, input: Iterable[IN], out: Collector[OUT])
}

Se puede usar así:

val input: DataStream[(String, Long)] = ...

input
    .keyBy(<key selector>)
    .window(<window assigner>)
    .apply(new MyWindowFunction())

¡Preste atención a la cuenta oficial 数据工匠记y concéntrese en los productos secos técnicos en tiempo real y fuera de línea en el campo de big data para compartir con regularidad! Sitio web personal www.lllpan.top
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/lp284558195/article/details/114581487
Recomendado
Clasificación