¿Qué es la ventana en Apache Flink?

Introducción a la transmisión de Windows en Apache Flink

Primero, comprendamos qué significa ventana en Flink.

Apache Flink es un procesador de flujo que tiene un mecanismo muy flexible para construir y evaluar ventanas sobre flujos de datos continuos. Para procesar DataStream infinito, se divide en sectores finitos basados ​​en algunos criterios, como las marcas de tiempo de elementos u otros criterios. Este concepto de Flink se llama windows . Esta división es necesaria cuando se realizan transformaciones que necesitan agregar elementos. Flink presenta definiciones de ventana muy flexibles que lo hacen sobresaliente entre otros procesadores de flujo de código abierto y crea una diferenciación entre Flink, Spark y Hadoop Map Reduce.

Necesitamos especificar una clave, un asignador de ventana y una función de ventana para una transformación en ventana. La clave se usa para crear las secuencias con clave lógica a partir de una secuencia infinita sin clave, mientras que el asignador de ventana se utiliza para asignar elementos a ventanas finitas por clave. Finalmente, para procesar los elementos de cada ventana, se utiliza la función de ventana.

La estructura básica de una transformación en ventana es la siguiente:

DataStream input = ...;

data.keyBy()

window()

.trigger()

.();

Veamos cada componente de las transformaciones en ventana en detalles a continuación:

Aprenda Flink de expertos de la industria

3. Asignadores de ventanas

Especifica la forma en que los elementos de flujo se dividen en sectores finitos. Algunos de los asignadores de ventanas preinstalados para Flink son ventanas giratorias, ventanas deslizantes, ventanas de sesión y ventanas globales, pero Flink le permite implementar su propia ventana extendiendo la clase de asignador de ventanas. Excepto las ventanas globales, el resto de los asignadores de ventanas incorporados asignan elementos según el tiempo de procesamiento o el tiempo del evento.

  • Los asignadores de tiempo de procesamiento asignan elementos en función del reloj actual de las máquinas de trabajo
  • Los asignadores de tiempo de evento asignan ventanas en función de las marcas de tiempo de los elementos.
  • El tiempo de ingestión es híbrido de procesamiento y tiempo de evento que asigna marcas de tiempo de reloj de pared a los registros a medida que llegan al sistema y continúa procesando con semántica de tiempo de eventos basada en las marcas de tiempo adjuntas.

Pero, ¿cómo difieren estas ventanas Flink? Veamos estas ventanas 1 por 1.

a. Ventanas globales

Aquí no se realiza la subdivisión de elementos en ventanas. En cambio, cada elemento se asigna a 1 sola ventana global por clave. Es útil solo si se especifica un activador personalizado sin el cual no se puede realizar ningún cálculo, ya que no tiene ningún final en el que se pueda realizar el cálculo.

Especifique ventanas globales de la siguiente manera:

data.keyBy()

.window(GlobalWindows.create())

.();

si. Ventanas caídas

Aquí se especifica el tamaño de la ventana y los elementos se asignan a ventanas que no se superponen. Por ej. Si se especifica que el tamaño de la ventana es de 2 minutos, todos los elementos en un tiempo de 2 minutos aparecerán en 1 ventana para su procesamiento. Algunas aplicaciones pueden requerir agregados suavizados para los cuales es importante que las ventanas no estén disjuntas.

Puede especificar las ventanas de tiempo del evento de caída de la siguiente manera:

data.keyBy()

.window(TumblingEventTimeWindows.of(Time.seconds(5)))

.();

C. Ventanas correderas

Asigna ventanas de tamaño fijo como en las ventanas giratorias, pero aquí las ventanas también pueden superponerse debido a que se puede asignar 1 elemento a varias ventanas. El tamaño de superposición se define mediante la diapositiva de la ventana de parámetros especificada por el usuario.

Por ejemplo, puede especificar un tamaño de ventana de 10 minutos con una diapositiva de 5 minutos. A continuación se utiliza para especificar ventanas de tiempo de eventos deslizantes:

data.keyBy()

.window(SlidingEventTimeWindows.of(Time.seconds(10), Time.seconds(5)))

.();

re. Windows de sesión

Esto es bueno en los casos en que los límites de las ventanas deben ajustarse según los datos entrantes. Con las ventanas de sesión es posible tener ventanas que comiencen en puntos individuales en el tiempo para cada tecla y que finalicen cuando haya existido cierto período de inactividad. Aquí, el parámetro de configuración es el intervalo de sesión que se utiliza para especificar cuánto tiempo esperar los nuevos datos antes de considerar una sesión como cerrada.

Puede especificar la ventana de sesión de tiempo de procesamiento de la siguiente manera:

data.keyBy()

.window(ProcessingTimeSessionWindows.withGap(Time.minutes(10)))

.();

Introduciendo Stream Windows en Apache Flink

El espacio de análisis de datos está presenciando una evolución del procesamiento por lotes a la transmisión para muchos casos de uso. Aunque el lote se puede manejar como un caso especial de procesamiento de flujo, el análisis de datos de transmisión sin fin a menudo requiere un cambio en la mentalidad y viene con su propia terminología (por ejemplo, “ventanas” y “al menos una vez” / “exactamente -una vez “). Este cambio y la nueva terminología pueden ser bastante confusos para las personas que son nuevas en el espacio del procesamiento de secuencias. Apache Flink es un procesador de flujo listo para producción con una API fácil de usar pero muy expresiva para definir programas avanzados de análisis de flujo. La API de Flink presenta definiciones de ventana muy flexibles en flujos de datos que le permiten destacarse entre otros procesadores de flujo de código abierto.

En esta publicación de blog, discutimos el concepto de ventanas para el procesamiento de secuencias, presentamos las ventanas integradas de Flink y explicamos su compatibilidad con la semántica de ventanas personalizada.

¿Qué son las ventanas y para qué sirven?

Considere el ejemplo de un sensor de tráfico que cuenta cada 15 segundos la cantidad de vehículos que pasan por una determinada ubicación. La secuencia resultante podría verse así:

Si desea saber cuántos vehículos pasaron esa ubicación, simplemente sumaría los recuentos individuales. Sin embargo, la naturaleza de un flujo de sensores es que produce datos continuamente. Dicha secuencia nunca termina y no es posible calcular una suma final que pueda devolverse. En cambio, es posible calcular sumas continuas, es decir, devolver para cada evento de entrada un registro de suma actualizado. Esto produciría una nueva corriente de sumas parciales.

Sin embargo, un flujo de sumas parciales podría no ser lo que estamos buscando, porque actualiza constantemente el recuento y, lo que es más importante, se pierde cierta información, como la variación a lo largo del tiempo. Por lo tanto, es posible que deseemos reformular nuestra pregunta y preguntar por la cantidad de autos que pasan la ubicación cada minuto. Esto requiere que agrupemos los elementos de la secuencia en conjuntos finitos, cada conjunto correspondiente a sesenta segundos. Esta operación se llama operación de caída de ventanas .

Las ventanas caídas discretizan una secuencia en ventanas que no se superponen. Para ciertas aplicaciones, es importante que las ventanas no estén separadas porque una aplicación puede requerir agregados suavizados. Por ejemplo, podemos calcular cada treinta segundos la cantidad de autos que pasaron en el último minuto. Tales ventanas se llaman ventanas deslizantes .

Definir ventanas en un flujo de datos como se discutió anteriormente es una operación no paralela. Esto se debe a que cada elemento de una secuencia debe ser procesado por el mismo operador de ventana que decide a qué ventanas se debe agregar el elemento. Windows en una transmisión completa se llama AllWindows en Flink. Para muchas aplicaciones, un flujo de datos debe agruparse en múltiples flujos lógicos en cada uno de los cuales se puede aplicar un operador de ventana. Piense, por ejemplo, en un flujo de recuentos de vehículos de múltiples sensores de tráfico (en lugar de solo un sensor como en nuestro ejemplo anterior), donde cada sensor monitorea una ubicación diferente. Al agrupar la transmisión por ID del sensor, podemos calcular estadísticas de tráfico en ventanas para cada ubicación en paralelo. En Flink, llamamos a estas ventanas particionadas simplemente Windows , ya que son el caso común de las secuencias distribuidas. La siguiente figura muestra ventanas (sensorId, count) que recopilan dos elementos sobre una secuencia de elementos de (sensorId, count) .

En términos generales, una ventana define un conjunto finito de elementos en una secuencia ilimitada. Este conjunto puede basarse en el tiempo (como en nuestros ejemplos anteriores), recuentos de elementos, una combinación de recuentos y tiempo, o alguna lógica personalizada para asignar elementos a las ventanas. La API DataStream de Flink proporciona operadores concisos para las operaciones de ventana más comunes, así como un mecanismo de ventanas genérico que permite a los usuarios definir una lógica de ventanas muy personalizada. A continuación, presentamos las ventanas de tiempo y conteo de Flink antes de analizar en detalle su mecanismo de ventanas.

Ventanas de tiempo

Como su nombre lo indica, las ventanas de tiempo agrupan elementos de flujo por tiempo. Por ejemplo, una ventana de tiempo de caída de un minuto recopila elementos durante un minuto y aplica una función a todos los elementos de la ventana después de transcurrido un minuto.

Definir ventanas de tiempo de caída y deslizamiento en Apache Flink es muy fácil:

// Secuencia de (sensorId, carCnt)
val vehicleCnts: DataStream [(Int, Int)] =…

val tumblingCnts: DataStream [(Int, Int)] = vehicleCnts
// secuencia clave por sensorId
.keyPor (0)
// ventana de tiempo de volteo de 1 minuto de duración
.timeWindow (Time.minutes (1))
// calcular suma sobre carCnt
.sum (1)

val slideCnts: DataStream [(Int, Int)] = vehicleCnts
.keyPor (0)
// ventana de tiempo deslizante de 1 minuto de duración y 30 segundos de intervalo de activación
.timeWindow (Time.minutes (1), Time.seconds (30))
.sum (1)

Hay un aspecto que aún no hemos discutido, a saber, el significado exacto de ” recoge elementos durante un minuto ” que se reduce a la pregunta, ” ¿Cómo interpreta el tiempo el procesador de flujo? “.

Apache Flink presenta tres nociones diferentes de tiempo, a saber , tiempo de procesamiento , tiempo de evento y tiempo de ingestión .

  1. En el tiempo de procesamiento , las ventanas se definen con respecto al reloj de pared de la máquina que construye y procesa una ventana, es decir, una ventana de tiempo de procesamiento de un minuto recoge elementos durante exactamente un minuto.
  2. En el momento del evento , las ventanas se definen con respecto a las marcas de tiempo que se adjuntan a cada registro de evento. Esto es común para muchos tipos de eventos, como entradas de registro, datos de sensores, etc., donde la marca de tiempo generalmente representa la hora en que ocurrió el evento. El tiempo del evento tiene varios beneficios sobre el tiempo de procesamiento. En primer lugar, desacopla la semántica del programa de la velocidad de servicio real de la fuente y el rendimiento de procesamiento del sistema. Por lo tanto, puede procesar datos históricos, que se sirven a la velocidad máxima, y ​​datos producidos continuamente con el mismo programa. También evita resultados semánticamente incorrectos en caso de contrapresión o demoras debido a la recuperación de fallas. En segundo lugar, las ventanas de tiempo de eventos calculan resultados correctos, incluso si los eventos llegan fuera de orden de su marca de tiempo, lo cual es común si un flujo de datos recopila eventos de fuentes distribuidas.
  3. El tiempo de ingestión es un híbrido de tiempo de procesamiento y evento. Asigna marcas de tiempo de reloj de pared a los registros tan pronto como llegan al sistema (en la fuente) y continúa procesando con semántica de tiempo de evento basada en las marcas de tiempo adjuntas.

Cuenta Windows

Apache Flink también cuenta con ventanas de conteo. Una ventana de recuento de vuelcos de 100 recopilará 100 eventos en una ventana y evaluará la ventana cuando se haya agregado el elemento número 100.

En la API DataStream de Flink, las ventanas de conteo rotativo y deslizante se definen de la siguiente manera:

// Secuencia de (sensorId, carCnt)
val vehicleCnts: DataStream [(Int, Int)] =…

val tumblingCnts: DataStream [(Int, Int)] = vehicleCnts
// secuencia clave por sensorId
.keyPor (0)
// ventana de recuento de volteo de 100 elementos de tamaño
.countWindow (100)
// calcula la suma de carCnt
.sum (1)

val slideCnts: DataStream [(Int, Int)] = vehicleCnts
.keyPor (0)
// ventana de recuento deslizante de 100 elementos de tamaño y 10 elementos de intervalo de activación
.countWindow (100, 10)
.sum (1)

Diseccionando la mecánica de ventanas de Flink

Las ventanas de tiempo y recuento incorporadas de Flink cubren una amplia gama de casos de uso de ventanas comunes. Sin embargo, hay aplicaciones que requieren una lógica de ventanas personalizada que las ventanas integradas de Flink no pueden abordar. Para admitir también aplicaciones que necesitan una semántica de ventanas muy específica, la API DataStream expone interfaces para las partes internas de su mecánica de ventanas. Estas interfaces dan un control muy detallado sobre la forma en que se construyen y evalúan las ventanas.

La siguiente figura muestra el mecanismo de ventanas de Flink e introduce los componentes involucrados.

Los elementos que llegan a un operador de ventana se entregan a un WindowAssigner . WindowAssigner asigna elementos a una o más ventanas, posiblemente creando nuevas ventanas. Una Window sí misma es solo un identificador para una lista de elementos y puede proporcionar cierta metainformación opcional, como la hora de inicio y finalización en el caso de una TimeWindow . Tenga en cuenta que se puede agregar un elemento a varias ventanas, lo que también significa que pueden existir varias ventanas al mismo tiempo.

Cada ventana posee un Trigger que decide cuándo se evalúa o purga la ventana. Se llama al disparador para cada elemento que se inserta en la ventana y cuando se agota el tiempo de espera de un temporizador registrado previamente. En cada evento, un disparador puede decidir disparar (es decir, evaluar), purgar (eliminar la ventana y descartar su contenido) o disparar y luego purgar la ventana. Un disparador que solo se dispara evalúa la ventana y la mantiene como está, es decir, todos los elementos permanecen en la ventana y se evalúan nuevamente cuando se dispara la próxima vez. Una ventana se puede evaluar varias veces y existe hasta que se purga. Tenga en cuenta que una ventana consume memoria hasta que se purga.

Cuando se dispara un disparador, la lista de elementos de la ventana se puede entregar a un Evictor opcional. El desalojador puede recorrer en iteración la lista y decidir cortar algunos elementos desde el comienzo de la lista, es decir, eliminar primero algunos de los elementos que ingresaron a la ventana. Los elementos restantes se asignan a una función de evaluación. Si no se definió ningún desalojador, el activador entrega todos los elementos de la ventana directamente a la función de evaluación.

La función de evaluación recibe los elementos de una ventana (posiblemente filtrada por un evictor) y calcula uno o más elementos de resultado para la ventana. La API DataStream acepta diferentes tipos de funciones de evaluación, incluidas funciones de agregación predefinidas como sum() , min() , max() , así como una función ReduceFunction , FoldFunction o WindowFunction . Una función de ventana es la función de evaluación más genérica y recibe el objeto de ventana (es decir, los metadatos de la ventana), la lista de elementos de ventana y la tecla de ventana (en el caso de una ventana con clave) como parámetros.

Estos son los componentes que constituyen la mecánica de ventanas de Flink. Ahora mostramos paso a paso cómo implementar una lógica de ventanas personalizada con la API DataStream. Comenzamos con un flujo de tipo DataStream[IN] y lo DataStream[IN] mediante una función de selector de teclas que extrae una clave de tipo KEY para obtener un KeyedStream[IN, KEY] .

entrada val: DataStream [IN] =…

// creó una secuencia con clave utilizando una función de selector de clave
val keyed: KeyedStream [IN, KEY] = entrada
.keyBy (myKeySel: (IN) => KEY)

Aplicamos un WindowAssigner[IN, WINDOW] que crea ventanas de tipo WINDOW dan como resultado WindowedStream[IN, KEY, WINDOW] . Además, un WindowAssigner también proporciona una implementación Trigger predeterminada.

// crea una secuencia en ventana usando un WindowAssigner
var windowed: WindowedStream [IN, KEY, WINDOW] = keyed
.window (myAssigner: WindowAssigner [IN, WINDOW])

Podemos especificar explícitamente un Trigger para sobrescribir el Trigger predeterminado proporcionado por WindowAssigner . Tenga en cuenta que especificar un activador no agrega una condición de activador adicional, sino que reemplaza el activador actual.

// anula el activador predeterminado de WindowAssigner
ventana = ventana
.trigger (myTrigger: Trigger [IN, WINDOW])

Es posible que queramos especificar un Evictor opcional de la siguiente manera.

// especifica un desalojador opcional
ventana = ventana
.evictor (myEvictor: Evictor [IN, WINDOW])

Finalmente, aplicamos una función WindowFunction que devuelve elementos de tipo OUT para obtener un DataStream[OUT] .

// aplica la función de ventana a la secuencia en ventana
salida val: DataStream [OUT] = en ventana
.apply (myWinFunc: WindowFunction [IN, OUT, KEY, WINDOW])

Con la mecánica de ventanas internas de Flink y su exposición a través de la API DataStream, es posible implementar una lógica de ventanas muy personalizada, como ventanas de sesión o ventanas que emiten resultados tempranos si los valores exceden un cierto umbral.

Conclusión

El soporte para varios tipos de ventanas sobre flujos de datos continuos es imprescindible para los procesadores de flujo modernos. Apache Flink es un procesador de flujo con un conjunto de características muy fuerte, que incluye un mecanismo muy flexible para construir y evaluar ventanas sobre flujos de datos continuos. Flink proporciona operadores de ventana predefinidos para casos de uso común, así como una caja de herramientas que permite definir una lógica de ventanas muy personalizada. La comunidad de Flink agregará más operadores de ventanas predefinidos a medida que aprendamos los requisitos de nuestros usuarios.

Apache Flink es un procesador de flujo que tiene un mecanismo muy flexible para construir y evaluar ventanas sobre flujos de datos continuos. Para procesar DataStream infinito, se divide en sectores finitos basados ​​en algunos criterios, como las marcas de tiempo de elementos u otros criterios. Este concepto de Flink se llama windows . Esta división es necesaria cuando se realizan transformaciones que necesitan agregar elementos. Flink presenta definiciones de ventana muy flexibles que lo hacen sobresaliente entre otros procesadores de flujo de código abierto y crea una diferenciación entre Flink, Spark y Hadoop Map Reduce.

Necesitamos especificar una clave, un asignador de ventana y una función de ventana para una transformación en ventana. La clave se usa para crear las secuencias con clave lógica a partir de una secuencia infinita sin clave, mientras que el asignador de ventana se utiliza para asignar elementos a ventanas finitas por clave. Finalmente, para procesar los elementos de cada ventana, se utiliza la función de ventana.

Lea la guía completa de ventanas en Flink.