Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

¿Qué podría hacer que una empresa tan grande como Lamoda, con un proceso simplificado y docenas de servicios interconectados, haga un cambio significativo en el enfoque? La motivación puede ser completamente diferente: desde legislativa hasta el deseo de experimentar inherente a todos los programadores.

Pero esto no significa que no pueda contar con beneficios adicionales. ¿Qué puede ganar exactamente si implementa la API basada en eventos en Kafka?, Sergey Zaika dirá (fewald). También habrá conos rellenos y descubrimientos interesantes: el experimento no puede prescindir de ellos.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Descargo de responsabilidad: este artículo se basa en materiales de la reunión que Sergey realizó en noviembre de 2018 en HighLoad++. La experiencia en vivo de Lamoda de trabajar con Kafka atrajo a los oyentes no menos que otros informes del programa. Nos parece que este es un gran ejemplo del hecho de que siempre es posible y necesario encontrar personas con ideas afines, y los organizadores de HighLoad ++ continuarán tratando de crear una atmósfera propicia para esto.

Sobre el proceso

Lamoda es una gran plataforma de comercio electrónico que tiene su propio centro de contacto, servicio de entrega (y muchos socios), un estudio fotográfico, un gran almacén y todo esto funciona en su propio software. Hay docenas de métodos de pago, socios b2b que pueden usar algunos o todos estos servicios y desean conocer la información más reciente sobre sus productos. Además, Lamoda opera en tres países además de la Federación Rusa, y allí todo es un poco diferente. En total, probablemente haya más de cien formas de configurar un nuevo pedido, que debe procesarse a su manera. Todo esto funciona con la ayuda de docenas de servicios que se comunican de formas a veces no obvias. También existe un sistema central cuya principal responsabilidad es el estado de los pedidos. La llamamos BOB, trabajo con ella.

Herramienta de reembolso con API basada en eventos

La palabra event-driven es bastante trillada, un poco más adelante definiremos con más detalle qué se entiende por esto. Comenzaré con el contexto en el que decidimos probar el enfoque de API basado en eventos en Kafka.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

En cualquier tienda, además de los pedidos que pagan los clientes, hay ocasiones en las que la tienda está obligada a devolver el dinero, porque el producto no le quedaba bien al cliente. Este es un proceso relativamente corto: aclaramos la información, si es necesario, y transferimos el dinero.

Pero la devolución se volvió más complicada debido a cambios en la legislación, y tuvimos que implementar un microservicio separado para ello.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Nuestra motivación:

  1. Ley FZ-54 - en resumen, la ley exige informar a la oficina de impuestos sobre cada transacción monetaria, ya sea una devolución o un recibo, en un SLA bastante corto de unos pocos minutos. Nosotros, como e-commerce, realizamos bastantes operaciones. Técnicamente, esto significa una nueva responsabilidad (y por lo tanto un nuevo servicio) y mejoras en todos los sistemas involucrados.
  2. división bob — un proyecto interno de la empresa para librar a BOB de una gran cantidad de responsabilidades secundarias y reducir su complejidad general.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Este diagrama muestra los principales sistemas Lamoda. Ahora la mayoría de ellos son más constelación de 5-10 microservicios alrededor de un monolito que se encoge. Están creciendo lentamente, pero estamos tratando de hacerlos más pequeños, porque desplegar un fragmento seleccionado en el medio da miedo, no puedes dejarlo caer. Nos vemos obligados a reservar todos los intercambios (flechas) y nos comprometemos a que alguno de ellos no esté disponible.

También hay bastantes intercambios en BOB: sistemas de pago, envíos, notificaciones, etc.

Técnicamente BOB es:

  • ~150k líneas de código + ~100k líneas de pruebas;
  • php7.2 + Zend 1 y Componentes Symfony 3;
  • >100 API y ~50 integraciones salientes;
  • 4 países con su propia lógica de negocio.

Implementar un BOB es costoso y doloroso, la cantidad de código y las tareas que resuelve es tal que nadie puede metérselo en la cabeza en su totalidad. En general, hay muchas razones para simplificarlo.

Proceso de devolución

Inicialmente, dos sistemas están involucrados en el proceso: BOB y Pago. Ahora hay dos más:

  • Servicio de Fiscalización, que atenderá los problemas de fiscalización y comunicación con los servicios externos.
  • Refund Tool, en la que simplemente se sacan nuevos canjes para no inflar el BOB.

Ahora el proceso se ve así:

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

  1. BOB recibe una solicitud de reembolso.
  2. BOB habla sobre esta herramienta de reembolso.
  3. La Herramienta de reembolso le dice a Pago: "Reembolso del dinero".
  4. El pago devuelve el dinero.
  5. Refund Tool y BOB sincronizan estados entre ellos, porque por ahora ambos lo necesitan. Todavía no estamos listos para cambiar por completo a la herramienta de reembolso, ya que BOB tiene una interfaz de usuario, informes para contabilidad y, en general, una gran cantidad de datos que no se pueden transferir tan fácilmente. Tienes que sentarte en dos sillas.
  6. Sale la solicitud de fiscalización.

Como resultado, creamos un determinado bus de eventos en Kafka: event-bus, en el que comenzó todo. Hurra, ahora tenemos un único punto de falla (sarcasmo).

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Los pros y los contras son bastante obvios. Hicimos un autobús, lo que significa que ahora todos los servicios dependen de él. Esto simplifica el diseño, pero introduce un único punto de falla en el sistema. Kafka caerá, el proceso se levantará.

¿Qué es la API basada en eventos?

Una buena respuesta a esta pregunta está en el informe de Martin Fowler (GOTO 2017) "Los muchos significados de la arquitectura impulsada por eventos".

Brevemente lo que hicimos:

  1. Envolvió todos los intercambios asíncronos a través de almacenamiento de eventos. En lugar de informar a cada consumidor interesado sobre un cambio de estado en la red, escribimos un evento de cambio de estado en una tienda centralizada y los consumidores interesados ​​en un tema leen todo lo que aparece allí.
  2. Evento (evento) en este caso es una notificación (notificaciones) que algo ha cambiado en alguna parte. Por ejemplo, el estado del pedido ha cambiado. Un consumidor que se preocupa por algunos datos que acompañan el cambio de estado y que no están en la notificación puede averiguar su estado por sí mismo.
  3. La opción máxima es el abastecimiento de eventos completo, transferencia de estado, en el que el evento contiene toda la información necesaria para el procesamiento: desde dónde y a qué estado cambiaron, cómo cambiaron exactamente los datos, etc. La única pregunta es la conveniencia y la cantidad de información que puede permitirse almacenar.

Como parte del lanzamiento de la Herramienta de reembolso, utilizamos la tercera opción. Esto simplifica el manejo de eventos porque no es necesario recuperar información detallada, además elimina el escenario en el que cada nuevo evento genera una ráfaga de solicitudes de búsqueda de los consumidores.

Herramienta de reembolso del servicio descargado, por lo que Kafka es más una prueba que una necesidad. No creo que si el servicio de reembolso se convirtiera en un proyecto de alta carga, el negocio sería feliz.

Intercambio asíncrono TAL CUAL

Para los intercambios asíncronos, el departamento de PHP suele utilizar RabbitMQ. Recopilamos los datos para la solicitud, los pusimos en cola y el consumidor del mismo servicio los contó y los envió (o no los envió). Para la API en sí, Lamoda usa activamente Swagger. Diseñamos la API, la describimos en Swagger, generamos código de cliente y servidor. También usamos JSON RPC 2.0 ligeramente extendido.

En algunos lugares se usan esb-buses, alguien vive en activeMQ, pero, en general, Conejo MQ - estándar.

Intercambio asíncrono PARA SER

Al diseñar un intercambio a través de events-bus, se puede rastrear una analogía. De manera similar, describimos el futuro intercambio de datos a través de descripciones de estructuras de eventos. El formato yaml, tuvimos que hacer la generación de código nosotros mismos, el generador crea DTO de acuerdo con la especificación y enseña a los clientes y servidores a trabajar con ellos. Generación entra en dos idiomas - golang y php. Esto mantiene las bibliotecas consistentes. El generador está escrito en golang, por lo que recibió el nombre de gogi.

El abastecimiento de eventos en Kafka es algo típico. Hay una solución de la versión empresarial principal de Kafka Confluent, hay nakadí, una solución de nuestros "hermanos" en el área de dominio Zalando. Nuestro motivación para empezar con vainilla Kafka es dejar la solución libre hasta que finalmente decidamos si la usaremos en todas partes, y también dejarnos margen de maniobra y mejoras: queremos apoyo para nuestros JSONRPC 2.0, generadores para dos idiomas ya ver qué más.

Es irónico que incluso en un caso tan feliz, cuando hay un negocio similar de Zalando que hizo una solución similar, no podemos usarla de manera efectiva.

Arquitectónicamente, al inicio, el patrón es el siguiente: leemos directamente desde Kafka, pero escribimos solo a través del bus de eventos. Hay muchas cosas preparadas para leer en Kafka: corredores, balanceadores, y está más o menos listo para escalar horizontalmente, quería mantenerlo. El registro es que queríamos envolver a través de un Gateway, también conocido como bus de eventos, y he aquí por qué.

Eventos-bus

O el autobús de eventos. Es solo una puerta de enlace http sin estado que asume varias funciones importantes:

  • Produciendo Validación - verificar que los eventos cumplan con nuestra especificación.
  • Sistema maestro por eventos, es decir, este es el principal y único sistema en la empresa que responde a la pregunta de qué eventos con qué estructuras se consideran válidas. La validación es solo tipos de datos y enumeraciones para la especificación de contenido rígido.
  • función hash para fragmentación: la estructura del mensaje de Kafka es clave-valor y se calcula a partir del hash de la clave donde colocarlo.

¿Por qué

Trabajamos en una gran empresa con un proceso simplificado. ¿Por qué cambiar algo? este es un experimentoy esperamos obtener varios beneficios.

Intercambios 1:n+1 (uno a muchos)

Con Kafka, es muy fácil conectar nuevos consumidores a la API.

Supongamos que tiene un directorio que debe mantenerse actualizado en varios sistemas a la vez (y en algunos nuevos). Anteriormente, inventamos un paquete que implementaba el set-API y la dirección de los consumidores se informaba al sistema maestro. Ahora el sistema maestro envía actualizaciones sobre el tema, y ​​todos los interesados ​​lo leen. Ha aparecido un nuevo sistema: lo firmaron sobre el tema. Sí, también un paquete, pero más simple.

En el caso de la refund-tool, que es una pieza de BOB, nos conviene mantenerlos sincronizados a través de Kafka. El pago dice que se devolvió el dinero: BOB, RT se enteraron, cambiaron de estado, el Servicio de Fiscalización se enteró y giraron un cheque.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Tenemos planes de hacer un único Servicio de Notificaciones que notificaría al cliente sobre las novedades en su pedido/devoluciones. Ahora esta responsabilidad se extiende a través de los sistemas. Nos bastará con enseñarle al Servicio de Notificaciones a captar información relevante de Kafka y responder a ella (y deshabilitar estas notificaciones en otros sistemas). No se requerirán nuevos intercambios directos.

Basado en datos

La información entre los sistemas se vuelve transparente, sin importar qué tan "empresa sangrienta" tenga y qué tan grande sea su trabajo pendiente. Lamoda tiene un departamento de análisis de datos que recopila datos de los sistemas y los transforma en un formato reutilizable tanto para sistemas empresariales como inteligentes. Kafka le permite proporcionarles rápidamente una gran cantidad de datos y mantener este flujo de información actualizado.

Registro de replicación

Los mensajes no desaparecen después de leerlos, como en RabbitMQ. Cuando el evento contiene suficiente información para procesar, tenemos un historial de los últimos cambios en el objeto y, si lo desea, la capacidad de aplicar estos cambios.

El período de almacenamiento del registro de replicación depende de la intensidad de la escritura en este tema. Kafka le permite establecer límites de forma flexible para el tiempo de almacenamiento y el volumen de datos. Para temas intensivos, es importante que todos los consumidores tengan tiempo para leer la información antes de que desaparezca, incluso en el caso de una inoperancia a corto plazo. Por lo general, es posible almacenar datos para unidades de dias, que es suficiente para un soporte.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Además, un pequeño recuento de la documentación, para aquellos que no están familiarizados con Kafka (la imagen también es de la documentación)

Hay colas en AMQP: escribimos mensajes en la cola para el consumidor. Por regla general, un sistema procesa una cola con la misma lógica empresarial. Si necesita notificar a varios sistemas, puede enseñar a la aplicación a escribir en varias colas o configurar un intercambio con un mecanismo de abanico que los clone.

Kafka tiene una abstracción similar tema, en el que escribes mensajes, pero no desaparecen después de leer. De manera predeterminada, cuando se conecta a Kafka, recibe todos los mensajes y tiene la opción de guardarlos donde los dejó. Es decir, lees secuencialmente, no puedes marcar el mensaje como leído, pero sí guardar el id, desde el cual puedes seguir leyendo más tarde. La identificación en la que se detuvo se llama compensación (offset), y el mecanismo se llama compensación de compromiso.

En consecuencia, se puede implementar una lógica diferente. Por ejemplo, tenemos BOB en 4 instancias para diferentes países: Lamoda está en Rusia, Kazajstán, Ucrania, Bielorrusia. Dado que se implementan por separado, tienen ligeramente sus propias configuraciones y su propia lógica comercial. Indicamos en el mensaje a qué país se refiere. Cada consumidor de BOB en cada país lee con un groupId diferente, y si el mensaje no se aplica a él, lo omiten, es decir. inmediatamente comete el desplazamiento +1. Si nuestro Servicio de pago lee el mismo tema, entonces lo hace con un grupo separado y, por lo tanto, las compensaciones no se cruzan.

Requisitos del evento:

  • Completitud de los datos. Me gustaría que el evento tenga suficientes datos para que pueda ser procesado.

  • Integridad Delegamos a Events-bus la comprobación de que el evento es consistente y que puede manejarlo.
  • El orden es importante. En el caso de una devolución, nos vemos obligados a trabajar con la historia. En las notificaciones no importa el pedido, si son notificaciones homogéneas el email será el mismo independientemente del pedido que llegue primero. En el caso de una devolución, hay un proceso claro, si cambia el pedido, surgirán excepciones, el reembolso no se creará ni procesará, terminaremos en un estado diferente.
  • Consistencia. Tenemos un repositorio y ahora creamos eventos en lugar de una API. Necesitamos una forma de enviar información de forma rápida y económica sobre nuevos eventos y cambios en los existentes a nuestros servicios. Esto se logra a través de una especificación común en un repositorio git y generadores de código separados. Por lo tanto, con nosotros se coordinan clientes y servidores en diferentes servicios.

Kafka en Lamoda

Tenemos tres instalaciones de Kafka:

  1. troncos;
  2. I + D;
  3. autobús de eventos.

Hoy estamos hablando solo del último punto. En events-bus, tenemos instalaciones no muy grandes: 3 corredores (servidores) y solo 27 temas. Como regla general, un tema es un proceso. Pero este es un punto sutil, y ahora lo tocaremos.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Arriba está el gráfico de rps. El proceso de reembolso está marcado con una línea turquesa (sí, la del eje x), y rosa es el proceso de actualización de contenido.

El catálogo de Lamoda contiene millones de productos y los datos se actualizan todo el tiempo. Algunas colecciones pasan de moda, se lanzan nuevas en su lugar, constantemente aparecen nuevos modelos en el catálogo. Tratamos de predecir qué les interesará a nuestros clientes mañana, por lo que constantemente compramos cosas nuevas, les tomamos fotos y actualizamos el escaparate.

Los picos rosas son actualizaciones de productos, es decir, cambios de productos. ¡Se puede ver que los chicos tomaron fotos, tomaron fotos y luego otra vez! — cargó un paquete de eventos.

Casos de uso de eventos de Lamoda

Usamos la arquitectura construida para las siguientes operaciones:

  • Seguimiento del estado de devolución: llamada a la acción y seguimiento de estado de todos los sistemas involucrados. Pago, estados, fiscalización, notificaciones. Aquí probamos el enfoque, creamos herramientas, recopilamos todos los errores, escribimos la documentación y les dijimos a nuestros colegas cómo usarlo.
  • Actualización de fichas de productos: configuración, metadatos, características. Un sistema lee (que muestra) y varios escriben.
  • Correo electrónico, push y sms: se armó el pedido, llegó el pedido, se aceptó la devolución, etc., son muchos.
  • Stock, renovación de almacén - actualización cuantitativa de nombres, solo números: recepción en el almacén, devolución. Es necesario que todos los sistemas asociados a la reserva de mercancías operen con los datos más actualizados. En este momento, el sistema de actualización del fregadero es bastante complejo, Kafka lo simplificará.
  • Análisis de Datos (departamento de I+D), herramientas de ML, analítica, estadística. Queremos que la información sea transparente: Kafka es ideal para esto.

Ahora la parte más interesante sobre bultos rellenos y descubrimientos interesantes que sucedieron en seis meses.

Problemas de diseño

Digamos que queremos hacer algo nuevo; por ejemplo, transferir todo el proceso de entrega a Kafka. Ahora parte del proceso está implementado en Order Processing en BOB. Detrás de la transferencia del pedido al servicio de entrega, el paso a un almacén intermedio, etc., existe un modelo de estado. Hay un monolito completo, incluso dos, además de un montón de API dedicadas a la entrega. Ellos saben mucho más sobre la entrega.

Estas parecen ser áreas similares, pero los estados son diferentes para Procesamiento de pedidos en BOB y para el sistema de entrega. Por ejemplo, algunos servicios de mensajería no envían estados intermedios, sino solo los finales: “entregado” o “perdido”. Otros, por el contrario, informan con gran detalle sobre el movimiento de mercancías. Todos tienen sus propias reglas de validación: para alguien, el correo electrónico es válido, lo que significa que será procesado; para otros, no es válido, pero el pedido aún se procesará, porque hay un teléfono para comunicarse, y alguien dirá que dicho pedido no se procesará en absoluto.

Flujo de datos

En el caso de Kafka, surge la cuestión de organizar el flujo de datos. Esta tarea está relacionada con la elección de la estrategia en varios puntos, repasemos todos.

¿En un tema o en diferentes?

Tenemos una especificación de eventos. En BOB, escribimos que tal o cual pedido necesita ser entregado, e indicamos: el número de pedido, su composición, algunos SKU y códigos de barra, etc. Cuando la mercancía llegue al almacén, la entrega podrá recibir estados, sellos de tiempo y todo lo que se necesite. Pero luego queremos recibir actualizaciones sobre estos datos en BOB. Tenemos un proceso inverso de obtención de datos desde la entrega. ¿Es el mismo evento? ¿O es un intercambio aparte que merece un tema aparte?

Lo más probable es que sean muy similares, y la tentación de hacer un tema no es descabellada, porque un tema separado significa consumidores separados, configuraciones separadas, una generación separada de todo esto. Pero no un hecho.

¿Nuevo campo o nuevo evento?

Pero si usa los mismos eventos, entonces surge otro problema. Por ejemplo, no todos los sistemas de entrega pueden generar un DTO que pueda generar un BOB. Les enviamos id, pero no los guardan, porque no los necesitan, y desde el punto de vista de iniciar el proceso event-bus, este campo es obligatorio.

Si introducimos una regla para el bus de eventos que requiere este campo, nos vemos obligados a establecer reglas de validación adicionales en el BOB o en el controlador de eventos de inicio. La validación comienza a extenderse por todo el servicio; esto no es muy conveniente.

Otro problema es la tentación del desarrollo incremental. Se nos dice que necesitamos agregar algo al evento, y tal vez, si pensamos detenidamente, debería haber sido un evento separado. Pero en nuestro esquema, un evento separado es un tema separado. Un tema aparte es todo el proceso que describí anteriormente. El desarrollador tiene la tentación de simplemente agregar un campo más al esquema JSON y regenerarlo.

En el caso de las devoluciones, llegamos al evento en medio año. Tuvimos un metaevento llamado actualización de reembolso, que tenía un campo de tipo que describía qué es realmente esta actualización. A partir de esto tuvimos interruptores “hermosos” con validadores que decían cómo validar este evento con este tipo.

Control de versiones de eventos

Para validar mensajes en Kafka, puede usar Avro, pero era necesario acostarse de inmediato y usar Confluent. En nuestro caso, tenemos que tener cuidado con el versionado. No siempre será posible volver a leer los mensajes del registro de replicación, porque el modelo se ha "ido". Básicamente, resulta construir versiones para que el modelo sea compatible con versiones anteriores: por ejemplo, hacer que un campo sea temporalmente opcional. Si las diferencias son demasiado fuertes, comenzamos a escribir en un tema nuevo y los clientes se trasplantan cuando terminan de leer el anterior.

Garantía de orden de lectura de particiones

Los temas dentro de Kafka se dividen en particiones. Esto no es muy importante mientras diseñamos entidades e intercambios, pero es importante cuando decidimos cómo consumirlo y escalarlo.

En el caso normal, le escribes un tema a Kafka. De forma predeterminada, se utiliza una partición y todos los mensajes de este tema se incluyen en ella. Y el consumidor lee estos mensajes secuencialmente, respectivamente. Digamos, ahora, que necesitamos expandir el sistema para que los mensajes sean leídos por dos consumidores diferentes. Si, por ejemplo, envía un SMS, puede decirle a Kafka que cree una partición adicional, y Kafka comenzará a descomponer los mensajes en dos partes: la mitad allí, la mitad allí.

¿Cómo los comparte Kafka? Cada mensaje tiene un cuerpo (en el que almacenamos JSON) y una clave. Puede adjuntar una función hash a esta clave, que determinará en qué partición caerá el mensaje.

En nuestro caso con los reembolsos, esto es importante, si tomamos dos particiones, existe la posibilidad de que el consumidor paralelo procese el segundo evento antes que el primero y habrá problemas. La función hash asegura que los mensajes con la misma clave terminen en la misma partición.

Eventos vs comandos

Este es otro problema con el que nos hemos encontrado. Un Evento es un tipo de evento: decimos que algo sucedió en algún lugar (algo_sucedió), por ejemplo, se canceló un artículo o se produjo un reembolso. Si alguien escucha estos eventos, entonces por "artículo cancelado" se creará la entidad de reembolso y "reembolso realizado" se registrará en algún lugar de las configuraciones.

Pero normalmente, cuando diseñas eventos, no quieres escribirlos en vano, estás apostando a que alguien los leerá. La tentación es alta de no escribir algo_sucedió (artículo_cancelado, reembolso_reembolsado), sino algo_debería_hacerse. Por ejemplo, el artículo está listo para ser devuelto.

Por un lado, sugiere cómo se utilizará el evento. Por otro lado, es mucho menos como un nombre de evento normal. Además, desde aquí no está lejos del comando do_something. Pero no tienes garantía de que alguien lea este evento; y si se lee, se lee con éxito; y si leyó con éxito, entonces hizo algo, y ese algo tuvo éxito. En el momento en que un evento se convierte en haz algo, la retroalimentación se vuelve necesaria, y ese es el problema.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

En un intercambio asíncrono en RabbitMQ, cuando lee un mensaje, va a http, tiene una respuesta, al menos que el mensaje fue aceptado. Cuando escribes a Kafka, hay un mensaje que le escribiste a Kafka, pero no sabes nada sobre cómo se procesó.

Por lo tanto, en nuestro caso, tuvimos que introducir un evento de respuesta y configurar el monitoreo para que, si volaban tantos eventos, después de tal o cual tiempo, llegara la misma cantidad de eventos de respuesta. Si no es así, entonces algo parece haber salido mal. Por ejemplo, si enviamos el evento "item_ready_to_refund", esperamos que se cree el reembolso, el cliente recibirá la devolución del dinero y el evento "money_refunded" nos llegará. Pero esto no es exacto, por lo que se necesita monitoreo.

Nuances

Hay un problema bastante obvio: si lees de un tema constantemente y tienes algún tipo de mensaje incorrecto, el consumidor colapsa y no vas más allá. Necesitas detener a todos los consumidores, confirme el desplazamiento adicional para continuar leyendo.

Lo sabíamos, apostamos por ello y, sin embargo, sucedió de todos modos. Y esto sucedió porque el evento era válido desde el punto de vista de events-bus, el evento era válido desde el punto de vista del validador de la aplicación, pero no era válido desde el punto de vista de PostgreSQL, porque en nuestro sistema MySQL con UNSIGNED INT, y en el sistema recién escrito PostgreSQL solo con INT. Él tiene un tamaño un poco más pequeño, y la identificación no encajaba. Symfony murió con una excepción. Por supuesto, detectamos la excepción, porque nos pusieron sobre ella e íbamos a cometer este desplazamiento, pero antes de eso queríamos incrementar el contador de problemas, ya que el mensaje se procesó sin éxito. Los contadores en este proyecto también están en la base, y Symfony ya ha cerrado la comunicación con la base, y la segunda excepción eliminó todo el proceso sin posibilidad de cometer la compensación.

Por un tiempo, el servicio dejó de funcionar; afortunadamente, con Kafka esto no da tanto miedo, porque los mensajes permanecen. Cuando se restaura la obra, se pueden leer. Es cómodo.

Kafka tiene la capacidad de establecer un desplazamiento arbitrario a través de herramientas. Pero para hacer esto, debe detener a todos los consumidores; en nuestro caso, prepare una versión separada en la que no haya consumidores, redistribuciones. Luego, Kafka puede cambiar el desplazamiento a través de las herramientas y el mensaje pasará.

Otro matiz - registro de replicación vs rdkafka.so - relacionado con las especificidades de nuestro proyecto. Tenemos PHP, y en PHP, por regla general, todas las bibliotecas se comunican con Kafka a través del repositorio rdkafka.so, y luego hay algún tipo de contenedor. Tal vez estas sean nuestras dificultades personales, pero resultó que simplemente releer un trozo de lo que ya se ha leído no es tan fácil. En general, hubo problemas de software.

Volviendo a las peculiaridades de trabajar con particiones, justo en la documentación está escrito consumidores >= particiones de temas. Pero me enteré mucho más tarde de lo que me gustaría. Si desea escalar y tener dos consumidores, necesita al menos dos particiones. Es decir, si tenía una partición en la que se acumularon 20 mil mensajes y creó una nueva, la cantidad de mensajes no se igualará con la misma rapidez. Por lo tanto, para tener dos consumidores paralelos, debe lidiar con las particiones.

Monitoreo

Creo que la forma en que lo controlemos aclarará aún más los problemas que existen en el enfoque existente.

Por ejemplo, contamos cuántos productos en la base de datos han cambiado recientemente su estado y, en consecuencia, los eventos deberían haber ocurrido en estos cambios, y enviamos este número a nuestro sistema de monitoreo. Luego, de Kafka obtenemos el segundo número, cuántos eventos se registraron realmente. Obviamente, la diferencia entre estos dos números siempre debe ser cero.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Además, debe monitorear cómo le está yendo al productor, si el bus de eventos ha recibido mensajes y cómo le está yendo al consumidor. Por ejemplo, en los gráficos a continuación, la Herramienta de reembolso está funcionando bien, pero BOB claramente tiene algunos problemas (picos azules).

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Ya mencioné el retraso del grupo de consumidores. En términos generales, este es el número de mensajes no leídos. En general, nuestros consumidores trabajan rápido, por lo que el lag suele ser 0, pero a veces puede haber un pico a corto plazo. Kafka puede hacer esto de inmediato, pero debe establecer un intervalo.

hay un proyecto Madriguera, que le dará más información sobre Kafka. Simplemente proporciona el estado a través de la API del grupo de consumidores, como lo ha hecho este grupo. Además de OK y Failed, hay una advertencia allí, y puede descubrir que sus consumidores no pueden seguir el ritmo de producción: no tienen tiempo para revisar lo que se está escribiendo. El sistema es bastante inteligente y fácil de usar.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Así es como se ve la respuesta de la API. Aquí el grupo bob-live-fifa, partición refund.update.v1, estado OK, lag 0 es el último desplazamiento final tal y cual.

Experiencia en el desarrollo del servicio Refund Tool con API asíncrona en Kafka

Monitoreo actualizado_en SLA (atascado) Ya mencioné. Por ejemplo, el artículo ha pasado al estado de que está listo para devolución. Configuramos Cron, que dice que si este objeto no se ha devuelto para reembolsar en 5 minutos (devolvemos el dinero a través de los sistemas de pago muy rápidamente), entonces definitivamente algo salió mal, y este es definitivamente un caso de soporte. Por lo tanto, simplemente tomamos Cron, que lee tales cosas, y si son mayores que 0, envía una alerta.

Para resumir, usar eventos es útil cuando:

  • varios sistemas necesitan información;
  • el resultado del procesamiento no es importante;
  • pocos o ningún evento.

Parecería que el artículo tiene un tema muy específico: una API asíncrona en Kafka, pero en relación con esto, me gustaría recomendar muchas cosas de inmediato.
Primero, siguiente HighLoad ++ pero hay que esperar hasta noviembre, en abril habrá su versión de San Petersburgo, y en junio hablaremos de cargas altas en Novosibirsk.
En segundo lugar, el autor del informe Sergey Zaika es miembro del Comité de Programa de nuestra nueva conferencia sobre gestión del conocimiento. ConocimientoConf. La conferencia es de un día, se llevará a cabo el 26 de abril, pero su programa es muy rico.
Y en mayo será PHP Rusia и RIT++ (con DevOpsConf como parte): allí también puede sugerir su tema, hablar sobre su experiencia y quejarse de sus conos rellenos.

Fuente: habr.com

Añadir un comentario