“Caminando en mis zapatos” - espera, ¿están marcados?

Desde 2019, Rusia cuenta con una ley sobre etiquetado obligatorio. La ley no se aplica a todos los grupos de productos y las fechas de entrada en vigor del etiquetado obligatorio para los grupos de productos son diferentes. El tabaco, el calzado y los medicamentos serán los primeros en estar sujetos a etiquetado obligatorio; después se añadirán otros productos, por ejemplo, perfumes, textiles y leche. Esta innovación legislativa impulsó el desarrollo de nuevas soluciones de TI que permitirán rastrear toda la cadena de vida de un producto desde la producción hasta la compra por parte del consumidor final, a todos los participantes en el proceso: tanto el propio Estado como todas las organizaciones que venden bienes con etiquetado obligatorio.

En X5, el sistema que rastreará los productos etiquetados e intercambiará datos con el estado y los proveedores se llama "Marcus". Le diremos en orden cómo y quién lo desarrolló, cuál es su tecnología y por qué tenemos algo de qué enorgullecernos.

“Caminando en mis zapatos” - espera, ¿están marcados?

Carga realmente alta

"Marcus" resuelve muchos problemas, el principal es la interacción de integración entre los sistemas de información X5 y el sistema de información estatal para productos etiquetados (GIS MP) para rastrear el movimiento de productos etiquetados. La plataforma también almacena todos los códigos de etiquetado que recibimos y el historial completo del movimiento de estos códigos entre objetos, y ayuda a eliminar la reclasificación de los productos etiquetados. Tomando como ejemplo los productos del tabaco, que se incluyeron en los primeros grupos de productos etiquetados, en un solo camión cargado de cigarrillos se pueden encontrar alrededor de 600 paquetes, cada uno de los cuales tiene su propio código único. Y la tarea de nuestro sistema es rastrear y verificar la legalidad de los movimientos de cada paquete entre almacenes y tiendas y, en última instancia, verificar la admisibilidad de su venta al comprador final. Y registramos alrededor de 000 transacciones en efectivo por hora, y también necesitamos registrar cómo llegó cada paquete a la tienda. Por tanto, teniendo en cuenta todos los movimientos entre objetos, esperamos decenas de miles de millones de registros al año.

equipo m

A pesar de que Marcus se considera un proyecto dentro de X5, se está implementando utilizando un enfoque de producto. El equipo trabaja según Scrum. El proyecto comenzó el verano pasado, pero los primeros resultados no llegaron hasta octubre: nuestro propio equipo estaba completamente armado, se desarrolló la arquitectura del sistema y se compró el equipo. Ahora el equipo cuenta con 16 personas, seis de las cuales están involucradas en el desarrollo backend y frontend, y tres de las cuales están involucradas en el análisis del sistema. Seis personas más participan en las pruebas manuales, de carga, automatizadas y en el mantenimiento del producto. Además, contamos con un especialista en SRE.

En nuestro equipo no solo los desarrolladores escriben código, casi todos los chicos saben programar y escribir pruebas automáticas, cargar scripts y scripts de automatización. Prestamos especial atención a esto, ya que incluso el soporte del producto requiere un alto nivel de automatización. Siempre intentamos asesorar y ayudar a compañeros que no han programado antes, y darles algunas pequeñas tareas en las que trabajar.

Debido a la pandemia de coronavirus, trasladamos a todo el equipo al trabajo remoto, la disponibilidad de todas las herramientas para la gestión del desarrollo, el flujo de trabajo integrado en Jira y GitLab hicieron posible pasar esta etapa fácilmente. Los meses transcurridos de forma remota demostraron que la productividad del equipo no se vio afectada; para muchos, la comodidad en el trabajo aumentó, lo único que faltaba era la comunicación en vivo.

Reunión de equipo remota

“Caminando en mis zapatos” - espera, ¿están marcados?

Reuniones durante el trabajo remoto

“Caminando en mis zapatos” - espera, ¿están marcados?

Pila de tecnología de la solución

El repositorio estándar y la herramienta CI/CD para X5 es GitLab. Lo usamos para almacenamiento de código, pruebas continuas e implementación en servidores de prueba y producción. También utilizamos la práctica de revisión de código, cuando al menos 2 colegas necesitan aprobar los cambios realizados por el desarrollador en el código. Los analizadores de código estático SonarQube y JaCoCo nos ayudan a mantener nuestro código limpio y garantizar el nivel requerido de cobertura de pruebas unitarias. Todos los cambios en el código deben pasar por estas comprobaciones. Todos los scripts de prueba que se ejecutan manualmente se automatizan posteriormente.

Para la implementación exitosa de los procesos de negocio por parte de “Marcus”, tuvimos que resolver una serie de problemas tecnológicos, cada uno en orden.

Tarea 1. La necesidad de escalabilidad horizontal del sistema.

Para resolver este problema, elegimos un enfoque de microservicio para la arquitectura. Al mismo tiempo, era muy importante comprender las áreas de responsabilidad de los servicios. Intentamos dividirlos en operaciones comerciales, teniendo en cuenta las particularidades de los procesos. Por ejemplo, la aceptación en un almacén no es una operación muy frecuente, pero sí de gran escala, durante la cual es necesario obtener rápidamente del regulador estatal información sobre las unidades de mercancías aceptadas, cuyo número en una entrega alcanza las 600000. , verifique la admisibilidad de aceptar este producto en el almacén y devuelva toda la información necesaria para el sistema de automatización del almacén. Pero el envío desde los almacenes tiene una intensidad mucho mayor, pero al mismo tiempo opera con pequeños volúmenes de datos.

Implementamos todos los servicios sin estado e incluso intentamos dividir las operaciones internas en pasos, utilizando lo que llamamos autotemas de Kafka. Esto es cuando un microservicio se envía un mensaje a sí mismo, lo que le permite equilibrar la carga en operaciones que consumen más recursos y simplifica el mantenimiento del producto, pero hablaremos de eso más adelante.

Decidimos separar los módulos para la interacción con sistemas externos en servicios separados. Esto permitió resolver el problema del cambio frecuente de API de sistemas externos, prácticamente sin impacto en los servicios con funcionalidad empresarial.

“Caminando en mis zapatos” - espera, ¿están marcados?

Todos los microservicios se implementan en un clúster de OpenShift, lo que resuelve tanto el problema de escalar cada microservicio como nos permite no utilizar herramientas de descubrimiento de servicios de terceros.

Tarea 2. La necesidad de mantener una carga elevada y un intercambio de datos muy intensivo entre los servicios de la plataforma: Sólo durante la fase de lanzamiento del proyecto se realizan unas 600 operaciones por segundo. Esperamos que este valor aumente a 5000 operaciones por segundo a medida que los puntos de venta minorista se conecten a nuestra plataforma.

Este problema se resolvió implementando un clúster Kafka y abandonando casi por completo la interacción sincrónica entre los microservicios de la plataforma. Esto requiere un análisis muy cuidadoso de los requisitos del sistema, ya que no todas las operaciones pueden ser asíncronas. Al mismo tiempo, no solo transmitimos eventos a través del corredor, sino que también transmitimos toda la información comercial requerida en el mensaje. Por tanto, el tamaño del mensaje puede alcanzar varios cientos de kilobytes. El límite de tamaño del mensaje en Kafka requiere que predigamos con precisión el tamaño del mensaje y, si es necesario, lo dividamos, pero la división es lógica y está relacionada con las operaciones comerciales.
Por ejemplo, dividimos en cajas los bienes que llegan en un automóvil. Para operaciones sincrónicas, se asignan microservicios separados y se llevan a cabo pruebas de carga exhaustivas. El uso de Kafka nos presentó otro desafío: probar el funcionamiento de nuestro servicio teniendo en cuenta la integración de Kafka hace que todas nuestras pruebas unitarias sean asíncronas. Resolvimos este problema escribiendo nuestros propios métodos de utilidad utilizando Embedded Kafka Broker. Esto no elimina la necesidad de escribir pruebas unitarias para métodos individuales, pero preferimos probar casos complejos usando Kafka.

Se prestó mucha atención al seguimiento de los registros para que su TraceId no se perdiera cuando se produjeran excepciones durante la operación de los servicios o al trabajar con el lote de Kafka. Y si no hubo problemas especiales con el primero, entonces en el segundo caso nos vemos obligados a registrar todos los TraceIds que vino con el lote y seleccionar uno para continuar con el rastreo. Luego, al buscar por el TraceId original, el usuario descubrirá fácilmente con cuál continuó el rastreo.

Tarea 3. La necesidad de almacenar una gran cantidad de datos: Más de mil millones de etiquetas al año sólo para tabaco llegan a X1. Requieren un acceso constante y rápido. En total, el sistema debe procesar alrededor de 5 mil millones de registros del historial de movimiento de estos productos etiquetados.

Para solucionar el tercer problema se optó por la base de datos NoSQL MongoDB. Hemos creado un fragmento de 5 nodos y cada nodo tiene un conjunto de réplicas de 3 servidores. Esto le permite escalar el sistema horizontalmente, agregar nuevos servidores al clúster y garantizar su tolerancia a fallas. Aquí nos encontramos con otro problema: garantizar la transaccionalidad en el clúster mongo, teniendo en cuenta el uso de microservicios escalables horizontalmente. Por ejemplo, una de las tareas de nuestro sistema es identificar intentos de revender productos con los mismos códigos de etiquetado. Aquí aparecen superposiciones con escaneos erróneos u operaciones erróneas por parte de los cajeros. Descubrimos que tales duplicados pueden ocurrir tanto dentro de un lote de Kafka que se procesa como dentro de dos lotes que se procesan en paralelo. Por lo tanto, la búsqueda de duplicados consultando la base de datos no arrojó nada. Para cada microservicio, resolvimos el problema por separado según la lógica empresarial de este servicio. Por ejemplo, para los cheques, agregamos un cheque dentro del lote y un procesamiento separado para la aparición de duplicados al insertar.

Para que el trabajo de los usuarios con el historial de operaciones no afecte de ninguna manera lo más importante: el funcionamiento de nuestros procesos comerciales, hemos separado todos los datos históricos en un servicio separado con una base de datos separada, que también recibe información a través de Kafka. . De esta manera, los usuarios trabajan con un servicio aislado sin afectar los servicios que procesan datos para las operaciones en curso.

Tarea 4: reprocesamiento y monitoreo de colas:

En los sistemas distribuidos, inevitablemente surgen problemas y errores en la disponibilidad de bases de datos, colas y fuentes de datos externas. En el caso de Marcus, la fuente de tales errores es la integración con sistemas externos. Era necesario encontrar una solución que permitiera solicitudes repetidas de respuestas erróneas con un tiempo de espera específico, pero que al mismo tiempo no dejara de procesar solicitudes exitosas en la cola principal. Para ello se optó por el concepto denominado “reintento basado en temas”. Para cada tema principal se crean uno o más temas de reintento a los que se envían mensajes erróneos y al mismo tiempo se elimina el retraso en el procesamiento de mensajes del tema principal. Esquema de interacción -

“Caminando en mis zapatos” - espera, ¿están marcados?

Para implementar dicho esquema, necesitábamos lo siguiente: integrar esta solución con Spring y evitar la duplicación de código. Mientras navegamos por la web, nos encontramos con una solución similar basada en Spring BeanPostProccessor, pero nos pareció innecesariamente engorrosa. Nuestro equipo ha creado una solución más simple que nos permite integrarnos en el ciclo de primavera para crear consumidores y, además, agregar reintentar consumidores. Ofrecimos un prototipo de nuestra solución al equipo de Spring, puedes verlo aquí. El número de Retry Consumers y el número de intentos de cada consumidor se configuran a través de parámetros, dependiendo de las necesidades del proceso de negocio, y para que todo funcione solo queda agregar la anotación org.springframework.kafka.annotation.KafkaListener , que es familiar para todos los desarrolladores de Spring.

Si el mensaje no se pudo procesar después de todos los reintentos, pasa a DLT (tema de mensajes no entregados) utilizando Spring DeadLetterPublishingRecoverer. A pedido de soporte, ampliamos esta funcionalidad y creamos un servicio separado que le permite ver mensajes incluidos en DLT, stackTrace, traceId y otra información útil sobre ellos. Además, se agregaron monitoreo y alertas a todos los temas DLT y ahora, de hecho, la aparición de un mensaje en un tema DLT es un motivo para analizar y corregir un defecto. Esto es muy conveniente: por el nombre del tema, entendemos inmediatamente en qué etapa del proceso surgió el problema, lo que acelera significativamente la búsqueda de su causa raíz.

“Caminando en mis zapatos” - espera, ¿están marcados?

Más recientemente, hemos implementado una interfaz que nos permite reenviar mensajes utilizando nuestro soporte después de eliminar sus causas (por ejemplo, restaurar la funcionalidad del sistema externo) y, por supuesto, establecer el defecto correspondiente para su análisis. Aquí es donde nuestros autotemas resultan útiles: para no reiniciar una larga cadena de procesamiento, puede reiniciarla desde el paso deseado.

“Caminando en mis zapatos” - espera, ¿están marcados?

Operación de la plataforma

La plataforma ya se encuentra en operación productiva, todos los días realizamos entregas y envíos, conectamos nuevos centros de distribución y tiendas. Como parte del piloto, el sistema trabaja con los grupos de productos “Tabaco” y “Zapatos”.

Todo nuestro equipo participa en la realización de pilotos, analiza problemas emergentes y hace sugerencias para mejorar nuestro producto, desde mejorar los registros hasta cambiar los procesos.

Para no repetir nuestros errores, todos los casos encontrados durante el piloto se reflejan en pruebas automatizadas. La presencia de una gran cantidad de pruebas automáticas y pruebas unitarias le permite realizar pruebas de regresión e instalar una revisión en literalmente unas pocas horas.

Ahora continuamos desarrollando y mejorando nuestra plataforma y enfrentando constantemente nuevos desafíos. Si estás interesado, hablaremos de nuestras soluciones en los siguientes artículos.

Fuente: habr.com

Añadir un comentario