Cómo dejar de preocuparte y empezar a vivir sin un monolito

Cómo dejar de preocuparte y empezar a vivir sin un monolito

A todos nos encantan las historias. Nos gusta sentarnos alrededor del fuego y hablar de nuestras victorias pasadas, batallas o simplemente de nuestra experiencia laboral.

Hoy es uno de esos días. E incluso si no estás junto al incendio en este momento, tenemos una historia para ti. La historia de cómo empezamos a trabajar con el almacenamiento en Tarantool.

Érase una vez, nuestra empresa tenía un par de "monolitos" y un "techo" para todos, a los que estos monolitos se acercaban lenta pero seguramente, limitando el vuelo de nuestra empresa, nuestro desarrollo. Y había un entendimiento claro: algún día tocaremos ese techo con fuerza.

Ahora prevalece la ideología de separar todo y a todos, desde los equipos hasta la lógica empresarial. Como resultado, tenemos, por ejemplo, dos DC que son prácticamente independientes a nivel de red. Y luego todo fue completamente diferente.

Hoy en día, existen muchas herramientas y herramientas para realizar cambios en forma de CI/CD, K8S, etc. En la época “monolítica”, no necesitábamos tantas palabras extranjeras. Bastaba con corregir el "almacenamiento" en la base de datos.

Pero el tiempo avanzó y la cantidad de solicitudes avanzó con él, lo que a veces disparó RPS más allá de nuestras capacidades. Con la entrada de los países de la CEI al mercado, la carga en el procesador de la base de datos del primer monolito no cayó por debajo del 90% y el RPS se mantuvo en el nivel de 2400. Y estos no eran solo pequeños selectores, sino también consultas importantes con un un montón de comprobaciones y JOIN que podrían ejecutarse para casi la mitad de los datos en el contexto de una gran IO.

Cuando las rebajas del Black Friday comenzaron a aparecer en escena, y Wildberries fue uno de los primeros en celebrarlas en Rusia, la situación se volvió completamente triste. Después de todo, la carga en esos días aumenta tres veces.
¡Oh, estos “tiempos monolíticos”! Estoy seguro de que a ti te ha pasado algo parecido y todavía no entiendes cómo te ha podido pasar esto.

¿Qué puedes hacer? La moda es inherente a la tecnología. Hace unos 5 años, tuvimos que repensar una de estas modificaciones en forma de un sitio existente en un servidor .NET y MS SQL, que almacenaba cuidadosamente toda la lógica del sitio en sí. Lo guardé con tanto cuidado que cortar un monolito así resultó ser un placer largo y nada fácil.
Una pequeña digresión.

En varios eventos digo: "¡si no viste un monolito, entonces no creciste!" Me interesa tu opinión sobre este asunto, por favor escríbela en los comentarios.

Y el trueno golpeó

Volvamos a nuestra "hoguera". Para distribuir la carga de funcionalidad "monolítica", decidimos dividir el sistema en microservicios basados ​​​​en tecnologías de código abierto. Porque, como mínimo, son más baratos de escalar. Y entendíamos al 100% que tendríamos que escalar (y mucho). Después de todo, ya en ese momento era posible ingresar a los mercados de los países vecinos, y el número de matriculaciones, así como el número de pedidos, comenzaron a crecer aún más.

Después de analizar los primeros candidatos para pasar del monolito a los microservicios, nos dimos cuenta de que el 80% de la escritura en ellos proviene de los sistemas back office y la lectura del front office. En primer lugar, se trataba de un par de subsistemas importantes para nosotros: los datos del usuario y un sistema para calcular el coste final de los productos basándose en información sobre descuentos y cupones adicionales para los clientes.

Sangrado. Ahora da miedo imaginarlo, pero además de los subsistemas mencionados anteriormente, también se eliminaron de nuestro monolito catálogos de productos, un carrito de compras para usuarios, un sistema de búsqueda de productos, un sistema de filtrado para catálogos de productos y varios tipos de sistemas de recomendación. Para el funcionamiento de cada uno de ellos, existen clases separadas de sistemas estrechamente adaptados, pero una vez todos vivían en una "casa".

Inmediatamente planeamos transferir datos sobre nuestros clientes al sistema fragmentado. La eliminación de la funcionalidad para calcular el costo final de los bienes requería una buena escalabilidad de lectura, ya que creaba la mayor carga de RPS y era la más difícil de implementar para la base de datos (hay muchos datos involucrados en el proceso de cálculo).

Como resultado, se nos ocurrió un esquema que encaja bien con Tarantool.

En ese momento, para el funcionamiento de los microservicios se eligieron esquemas para trabajar con varios centros de datos en máquinas virtuales y de hardware. Como se muestra en las figuras, las opciones de replicación de Tarantool se aplicaron tanto en modo maestro-maestro como maestro-esclavo.

Cómo dejar de preocuparte y empezar a vivir sin un monolito
Arquitectura. Opción 1. Servicio al usuario

Actualmente, hay 24 fragmentos, cada uno de los cuales tiene 2 instancias (una para cada DC), todas en modo maestro-maestro.

Encima de la base de datos hay aplicaciones que acceden a las réplicas de la base de datos. Las aplicaciones funcionan con Tarantool a través de nuestra biblioteca personalizada, que implementa la interfaz del controlador Tarantool Go. Ella ve todas las réplicas y puede trabajar con el maestro para leer y escribir. Básicamente, implementa el modelo de conjunto de réplicas, que agrega lógica para seleccionar réplicas, realizar reintentos, un disyuntor y un límite de velocidad.

En este caso, es posible configurar la política de selección de réplicas en el contexto de fragmentos. Por ejemplo, todos contra todos.

Cómo dejar de preocuparte y empezar a vivir sin un monolito
Arquitectura. Opción 2. Servicio de cálculo del coste final de la mercancía.

Hace unos meses, la mayoría de las solicitudes para calcular el coste final de la mercancía se dirigieron a un nuevo servicio que, en principio, funciona sin bases de datos, pero hace un tiempo todo fue procesado al 100% por un servicio con Tarantool bajo el capó.

La base de datos del servicio consta de 4 maestros en los que el sincronizador recopila datos y cada uno de estos maestros de replicación distribuye datos a réplicas de solo lectura. Cada maestro tiene aproximadamente 15 réplicas de este tipo.

Ya sea en el primero o en el segundo esquema, si un DC no está disponible, la aplicación puede recibir datos en el segundo.

Vale la pena señalar que la replicación en Tarantool es bastante flexible y se puede configurar en tiempo de ejecución. En otros sistemas surgieron dificultades. Por ejemplo, cambiar los parámetros max_wal_senders y max_replication_slots en PostgreSQL requiere reiniciar el asistente, lo que en algunos casos puede provocar la interrupción de las conexiones entre la aplicación y el DBMS.

Busca y encuentra!

¿Por qué no lo hicimos “como la gente normal”, sino que elegimos una forma atípica? Depende de lo que se considere normal. Muchas personas generalmente crean un clúster a partir de Mongo y lo distribuyen en tres centros de datos distribuidos geográficamente.

En ese momento ya teníamos dos proyectos de Redis. El primero era un caché y el segundo era un almacenamiento persistente para datos no demasiado críticos. Fue bastante difícil con él, en parte por culpa nuestra. A veces, había volúmenes bastante grandes en la llave y, de vez en cuando, el sitio empeoraba. Usamos este sistema en la versión maestro-esclavo. Y hubo muchos casos en los que algo le pasó al maestro y la replicación falló.

Es decir, Redis es bueno para tareas sin estado, no para tareas con estado. En principio, permitía resolver la mayoría de los problemas, pero sólo si eran soluciones clave-valor con un par de índices. Pero Redis en ese momento estaba bastante triste por la persistencia y la replicación. Además, hubo quejas sobre el rendimiento.

Pensamos en MySQL y PostgreSQL. Pero el primero de alguna manera no nos gustó, y el segundo es un producto bastante sofisticado en sí mismo, y no sería apropiado construir servicios simples sobre él.
Probamos RIAK, Cassandra e incluso una base de datos gráfica. Todas estas son soluciones bastante específicas que no eran adecuadas para el papel de herramienta universal general para la creación de servicios.

Al final nos decidimos por Tarantool.

Recurrimos a él cuando estaba en la versión 1.6. Nos interesó la simbiosis entre clave-valor y la funcionalidad de una base de datos relacional. Hay índices secundarios, transacciones y espacios, estos son como tablas, pero no simples, puedes almacenar diferentes números de columnas en ellas. Pero la característica principal de Tarantool fueron los índices secundarios combinados con valor-clave y transaccionalidad.

La receptiva comunidad de habla rusa, dispuesta a ayudar en el chat, también jugó un papel importante. Usamos esto activamente y vivimos directamente en el chat. Y no se olvide de una perseverancia decente sin errores ni pifias evidentes. Si miras nuestra historia con Tarantool, tuvimos muchos problemas y fallas con la replicación, ¡pero nunca perdimos datos debido a su falla!

La implementación tuvo un comienzo difícil

En ese momento, nuestra principal pila de desarrollo era .NET, para el cual no existía ningún conector para Tarantool. Inmediatamente comenzamos a hacer algo en Go. También funcionó bien con Lua. El principal problema en ese momento era la depuración: en .NET todo es genial con esto, pero después de eso fue difícil sumergirse en el mundo de Lua integrado, cuando no tienes depuración excepto los registros. Además, por alguna razón la replicación fallaba periódicamente, por lo que tuve que profundizar en la estructura del motor Tarantool. En esto ayudó el chat y en menor medida la documentación, a veces mirábamos el código. En ese momento, la documentación era regular.

Entonces, en el transcurso de varios meses, logré entender y obtener resultados decentes al trabajar con Tarantool. Recopilamos desarrollos de referencia en git que ayudaron con la formación de nuevos microservicios. Por ejemplo, cuando surgió una tarea: crear otro microservicio, el desarrollador miró el código fuente de la solución de referencia en el repositorio y no tomó más de una semana crear uno nuevo.

Eran tiempos especiales. Convencionalmente, podrías acercarte al administrador de la mesa de al lado y preguntarle: "Dame una máquina virtual". Unos treinta minutos más tarde el coche ya estaba contigo. Usted se conectó, instaló todo y se le envió tráfico.

Hoy esto ya no funcionará: es necesario agregar monitoreo y registro al servicio, cubrir la funcionalidad con pruebas, solicitar una máquina virtual o la entrega a Kuber, etc. En general será mejor así, aunque llevará más tiempo y será más problemático.

Divide y vencerás. ¿Cuál es el problema con Lua?

Hubo un serio dilema: algunos equipos no pudieron implementar cambios de manera confiable en un servicio con mucha lógica en Lua. Esto a menudo iba acompañado de que el servicio no funcionaba.

Es decir, los desarrolladores están preparando algún tipo de cambio. Tarantool comienza a realizar la migración, pero la réplica todavía tiene el código anterior; Algún DDL u otra cosa llega allí a través de la replicación y el código simplemente se desmorona porque no se tiene en cuenta. Como resultado, el procedimiento de actualización para los administradores se estableció en una hoja A4: detener la replicación, actualizar esto, activar la replicación, desactivar aquí, actualizar allí. ¡Pesadilla!

Como resultado, ahora la mayoría de las veces intentamos no hacer nada en Lua. Simplemente use iproto (un protocolo binario para interactuar con el servidor) y listo. Quizás esto sea una falta de conocimiento entre los desarrolladores, pero desde este punto de vista el sistema es complejo.

No siempre seguimos ciegamente este guión. Hoy no tenemos blanco y negro: o todo está en Lua o todo está en Go. Ya entendemos cómo podemos combinarlos para no terminar con problemas de migración más adelante.

¿Dónde está Tarantool ahora?
Tarantool se utiliza en el servicio para calcular el coste final de los bienes teniendo en cuenta los cupones de descuento, también conocido como "Promotor". Como dije antes, ahora se jubila: lo reemplazan un nuevo servicio de catálogo con precios precalculados, pero hace seis meses todos los cálculos se hacían en Promotizer. Anteriormente, la mitad de su lógica estaba escrita en Lua. Hace dos años, el servicio se convirtió en una instalación de almacenamiento y la lógica se reescribió en Go, porque la mecánica de los descuentos había cambiado un poco y el servicio carecía de rendimiento.

Uno de los servicios más críticos es el perfil de usuario. Es decir, todos los usuarios de Wildberries están almacenados en Tarantool, y hay alrededor de 50 millones de ellos, un sistema fragmentado por ID de usuario, distribuido en varios DC conectados a los servicios Go.
Según RPS, Promoter alguna vez fue líder, alcanzando 6 mil solicitudes. En un momento tuvimos entre 50 y 60 copias. Ahora el líder en RPS son los perfiles de usuario, alrededor de 12 20. Este servicio utiliza fragmentación personalizada, dividida por rangos de ID de usuario. El servicio atiende a más de 4 máquinas, pero son demasiadas, planeamos reducir los recursos asignados, porque la capacidad de 5-XNUMX máquinas es suficiente para ello.

El servicio de sesión es nuestro primer servicio en vshard y Cartucho. Configurar vshard y actualizar Cartucho requirió un poco de esfuerzo por nuestra parte, pero al final todo salió bien.

El servicio para mostrar diferentes banners en el sitio web y en la aplicación móvil fue uno de los primeros que se lanzó directamente en Tarantool. Este servicio destaca por tener entre 6 y 7 años, todavía está en funcionamiento y nunca se ha reiniciado. Se utilizó replicación maestro-maestro. Nunca se rompió nada.

Hay un ejemplo del uso de Tarantool para la funcionalidad de referencia rápida en un sistema de almacén para verificar rápidamente la información en algunos casos. Intentamos usar Redis para esto, pero los datos en la memoria ocuparon más espacio que Tarantool.

Los servicios de lista de espera, suscripciones de clientes, historias de moda actualmente y productos diferidos también funcionan con Tarantool. El último servicio en memoria ocupa unos 120 GB. Este es el servicio más completo de los anteriores.

Conclusión

Gracias a los índices secundarios combinados con clave-valor y transaccionalidad, Tarantool es muy adecuado para arquitecturas basadas en microservicios. Sin embargo, encontramos dificultades al implementar cambios en servicios con mucha lógica en Lua: los servicios a menudo dejaban de funcionar. No pudimos superar esto y con el tiempo llegamos a diferentes combinaciones de Lua y Go: sabemos dónde usar un idioma y dónde usar otro.

¿Qué más leer sobre el tema?

Fuente: habr.com

Añadir un comentario