No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

¡Hola, Habr!

Te recordamos que siguiendo el libro sobre Kafka Hemos publicado un trabajo igualmente interesante sobre la biblioteca. API de Kafka Streams.

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Por ahora, la comunidad apenas está aprendiendo los límites de esta poderosa herramienta. Recientemente se publicó un artículo cuya traducción nos gustaría presentarles. Desde su propia experiencia, el autor cuenta cómo convertir Kafka Streams en un almacenamiento de datos distribuido. ¡Disfruta leyendo!

biblioteca apache Corrientes de Kafka utilizado en todo el mundo en empresas para el procesamiento de flujo distribuido sobre Apache Kafka. Uno de los aspectos subestimados de este marco es que le permite almacenar el estado local producido en función del procesamiento de subprocesos.

En este artículo, le contaré cómo nuestra empresa logró aprovechar esta oportunidad de manera rentable al desarrollar un producto para la seguridad de aplicaciones en la nube. Utilizando Kafka Streams, creamos microservicios de estado compartido, cada uno de los cuales sirve como una fuente de información confiable, tolerante a fallas y altamente disponible sobre el estado de los objetos en el sistema. Para nosotros, esto es un paso adelante tanto en términos de confiabilidad como de facilidad de soporte.

Si está interesado en un enfoque alternativo que le permita utilizar una única base de datos central para respaldar el estado formal de sus objetos, léalo, será interesante...

Por qué pensamos que era hora de cambiar la forma en que trabajamos con el estado compartido

Necesitábamos mantener el estado de varios objetos según los informes de los agentes (por ejemplo: ¿el sitio estaba bajo ataque)? Antes de migrar a Kafka Streams, a menudo dependíamos de una única base de datos central (+ API de servicio) para la gestión del estado. Este enfoque tiene sus inconvenientes: situaciones intensivas de citas Mantener la coherencia y la sincronización se convierte en un verdadero desafío. La base de datos puede convertirse en un cuello de botella o terminar en condición de carrera y sufren de imprevisibilidad.

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 1: Un escenario típico de estado dividido visto antes de la transición a
Kafka y Kafka Streams: los agentes comunican sus vistas a través de API, el estado actualizado se calcula a través de una base de datos central

Conozca Kafka Streams, que facilita la creación de microservicios estatales compartidos

Hace aproximadamente un año, decidimos analizar detenidamente nuestros escenarios estatales compartidos para abordar estos problemas. Inmediatamente decidimos probar Kafka Streams: sabemos cuán escalable, altamente disponible y tolerante a fallas, y cuán rica es su funcionalidad de transmisión (transformaciones, incluidas las con estado). Justo lo que necesitábamos, sin mencionar lo maduro y confiable que se ha vuelto el sistema de mensajería en Kafka.

Cada uno de los microservicios con estado que creamos se creó sobre una instancia de Kafka Streams con una topología bastante simple. Consistía en 1) una fuente 2) un procesador con un almacén clave-valor persistente 3) un sumidero:

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 2: La topología predeterminada de nuestras instancias de transmisión para microservicios con estado. Tenga en cuenta que aquí también hay un repositorio que contiene metadatos de planificación.

En este nuevo enfoque, los agentes redactan mensajes que se introducen en el tema de origen y los consumidores (por ejemplo, un servicio de notificación por correo) reciben el estado compartido calculado a través del receptor (tema de salida).

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 3: Nuevo flujo de tareas de ejemplo para un escenario con microservicios compartidos: 1) el agente genera un mensaje que llega al tema fuente de Kafka; 2) un microservicio con estado compartido (usando Kafka Streams) lo procesa y escribe el estado calculado en el tema final de Kafka; después de lo cual 3) los consumidores aceptan el nuevo estado

Oye, ¡este almacén clave-valor integrado es realmente muy útil!

Como se mencionó anteriormente, nuestra topología de estado compartido contiene un almacén de valores clave. Encontramos varias opciones para usarlo y dos de ellas se describen a continuación.

Opción n.º 1: utilizar un almacén de valores clave para los cálculos

Nuestro primer almacén clave-valor contenía los datos auxiliares que necesitábamos para los cálculos. Por ejemplo, en algunos casos el estado compartido estaba determinado por el principio de "votos mayoritarios". El repositorio podría contener todos los informes más recientes de los agentes sobre el estado de algún objeto. Luego, cuando recibiéramos un nuevo informe de un agente u otro, podríamos guardarlo, recuperar informes de todos los demás agentes sobre el estado del mismo objeto del almacenamiento y repetir el cálculo.
La Figura 4 a continuación muestra cómo expusimos el almacén de clave/valor al método de procesamiento del procesador para que luego se pudiera procesar el nuevo mensaje.

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Ilustración 4: Abrimos el acceso al almacén clave-valor para el método de procesamiento del procesador (después de esto, cada script que trabaje con estado compartido debe implementar el método doProcess)

Opción n.º 2: crear una API CRUD sobre Kafka Streams

Una vez establecido nuestro flujo de tareas básico, comenzamos a intentar escribir una API RESTful CRUD para nuestros microservicios de estado compartido. Queríamos poder recuperar el estado de algunos o todos los objetos, así como establecer o eliminar el estado de un objeto (útil para soporte de backend).

Para admitir todas las API de Get State, cada vez que necesitábamos volver a calcular el estado durante el procesamiento, lo almacenamos en un almacén de valores clave integrado durante mucho tiempo. En este caso, resulta bastante sencillo implementar dicha API utilizando una única instancia de Kafka Streams, como se muestra en la siguiente lista:

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 5: Uso del almacén clave-valor integrado para obtener el estado precalculado de un objeto

Actualizar el estado de un objeto a través de la API también es fácil de implementar. Básicamente, todo lo que necesitas hacer es crear un productor Kafka y usarlo para crear un registro que contenga el nuevo estado. Esto garantiza que todos los mensajes generados a través de la API se procesarán de la misma manera que los recibidos de otros productores (por ejemplo, agentes).

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 6: Puede establecer el estado de un objeto utilizando el productor Kafka

Pequeña complicación: Kafka tiene muchas particiones

A continuación, queríamos distribuir la carga de procesamiento y mejorar la disponibilidad proporcionando un grupo de microservicios de estado compartido por escenario. La configuración fue muy sencilla: una vez que configuramos todas las instancias para que se ejecutaran con el mismo ID de aplicación (y los mismos servidores de arranque), casi todo lo demás se hizo automáticamente. También especificamos que cada tema de origen constaría de varias particiones, de modo que a cada instancia se le podría asignar un subconjunto de dichas particiones.

También mencionaré que es una práctica común realizar una copia de seguridad del almacén estatal para, por ejemplo, en caso de recuperación después de un fallo, transferir esta copia a otra instancia. Para cada almacén de estado en Kafka Streams, se crea un tema replicado con un registro de cambios (que rastrea las actualizaciones locales). Así, Kafka respalda constantemente el almacén estatal. Por lo tanto, en caso de falla de una u otra instancia de Kafka Streams, el almacén de estado se puede restaurar rápidamente en otra instancia, donde irán las particiones correspondientes. Nuestras pruebas han demostrado que esto se hace en cuestión de segundos, incluso si hay millones de registros en la tienda.

Al pasar de un único microservicio con estado compartido a un grupo de microservicios, resulta menos trivial implementar la API Get State. En la nueva situación, el almacén de estado de cada microservicio contiene solo una parte de la imagen general (aquellos objetos cuyas claves se asignaron a una partición específica). Tuvimos que determinar qué instancia contenía el estado del objeto que necesitábamos, y lo hicimos basándonos en los metadatos del hilo, como se muestra a continuación:

No solo procesamiento: cómo creamos una base de datos distribuida a partir de Kafka Streams y qué surgió de ella

Figura 7: Utilizando metadatos de flujo, determinamos desde qué instancia consultar el estado del objeto deseado; Se utilizó un enfoque similar con la API GET ALL.

Resultados clave

Las tiendas estatales en Kafka Streams pueden servir como una base de datos distribuida de facto,

  • constantemente replicado en Kafka
  • Se puede crear fácilmente una API CRUD sobre dicho sistema
  • Manejar múltiples particiones es un poco más complicado
  • También es posible agregar uno o más almacenes de estado a la topología de transmisión para almacenar datos auxiliares. Esta opción se puede utilizar para:
  • Almacenamiento a largo plazo de los datos necesarios para los cálculos durante el procesamiento del flujo
  • Almacenamiento a largo plazo de datos que pueden ser útiles la próxima vez que se aprovisione la instancia de streaming
  • mucho más...

Estas y otras ventajas hacen que Kafka Streams sea muy adecuado para mantener el estado global en un sistema distribuido como el nuestro. Kafka Streams ha demostrado ser muy confiable en producción (prácticamente no hemos tenido pérdida de mensajes desde que lo implementamos) y estamos seguros de que sus capacidades no se detendrán allí.

Fuente: habr.com

Añadir un comentario