La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

¡Hola a todos! Mi nombre es Sergey Kostanbaev, en la Bolsa estoy desarrollando el núcleo del sistema comercial.

Cuando las películas de Hollywood muestran la Bolsa de Nueva York, siempre se ve así: multitudes de gente, todo el mundo grita algo, agita papeles, se produce un caos total. Esto nunca ha sucedido aquí en la Bolsa de Moscú, porque las operaciones se han realizado electrónicamente desde el principio y se basan en dos plataformas principales: Spectra (mercado de divisas) y ASTS (mercado de divisas, acciones y dinero). Y hoy quiero hablar sobre la evolución de la arquitectura del sistema de negociación y compensación ASTS, sobre diversas soluciones y hallazgos. La historia será larga, así que tuve que dividirla en dos partes.

Somos uno de los pocos intercambios en el mundo que comercializa activos de todas las clases y brinda una gama completa de servicios de intercambio. Por ejemplo, el año pasado ocupamos el segundo lugar en el mundo en términos de volumen de negociación de bonos, el puesto 25 entre todas las bolsas de valores y el puesto 13 en capitalización entre las bolsas públicas.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

Para los operadores comerciales profesionales, parámetros como el tiempo de respuesta, la estabilidad de la distribución del tiempo (jitter) y la confiabilidad de todo el complejo son críticos. Actualmente procesamos decenas de millones de transacciones por día. El procesamiento de cada transacción por parte del núcleo del sistema lleva decenas de microsegundos. Por supuesto, los operadores móviles en Nochevieja o los propios motores de búsqueda tienen una carga de trabajo mayor que la nuestra, pero en términos de carga de trabajo, sumado a las características antes mencionadas, me parece que pocos se pueden comparar con nosotros. Al mismo tiempo, es importante para nosotros que el sistema no se ralentice ni un segundo, funcione de forma absolutamente estable y todos los usuarios estén en igualdad de condiciones.

Una pequeña historia

En 1994, se lanzó el sistema australiano ASTS en la Bolsa Interbancaria de Divisas de Moscú (MICEX), y desde ese momento se puede contar la historia rusa del comercio electrónico. En 1998, se modernizó la arquitectura del intercambio para introducir el comercio por Internet. Desde entonces, la velocidad de implementación de nuevas soluciones y cambios arquitectónicos en todos los sistemas y subsistemas no ha hecho más que ganar impulso.

En aquellos años, el sistema de intercambio funcionaba con hardware de alta gama: servidores HP Superdome 9000 ultrafiables (basados ​​en el PA-RISC), en el que estaba absolutamente todo duplicado: subsistemas de entrada/salida, red, RAM (de hecho, había una matriz RAID de RAM), procesadores (intercambiables en caliente). Era posible cambiar cualquier componente del servidor sin detener la máquina. Confiamos en estos dispositivos y los consideramos prácticamente a prueba de fallos. El sistema operativo era un sistema HP UX similar a Unix.

Pero aproximadamente desde 2010, ha surgido un fenómeno llamado comercio de alta frecuencia (HFT), o comercio de alta frecuencia; en pocas palabras, robots de bolsa. En sólo dos años y medio, la carga de nuestros servidores se ha multiplicado por 2,5.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

Era imposible soportar tal carga con la arquitectura y el equipamiento antiguos. Era necesario adaptarse de alguna manera.

principio

Las solicitudes al sistema de cambio se pueden dividir en dos tipos:

  • Actas. Si desea comprar dólares, acciones o cualquier otra cosa, envía una transacción al sistema comercial y recibe una respuesta sobre el éxito.
  • Solicitudes de información. Si desea conocer el precio actual, consulte el libro de pedidos o los índices y luego envíe solicitudes de información.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

Esquemáticamente, el núcleo del sistema se puede dividir en tres niveles:

  • El nivel de cliente, en el que trabajan los corredores y los clientes. Todos interactúan con los servidores de acceso.
  • Los servidores de puerta de enlace son servidores de almacenamiento en caché que procesan localmente todas las solicitudes de información. ¿Quiere saber a qué precio se cotizan actualmente las acciones de Sberbank? La solicitud va al servidor de acceso.
  • Pero si desea comprar acciones, la solicitud se envía al servidor central (Trade Engine). Hay un servidor de este tipo para cada tipo de mercado, ellos juegan un papel vital, es para ellos que creamos este sistema.

El núcleo del sistema comercial es una base de datos inteligente en memoria en la que todas las transacciones son transacciones de intercambio. La base estaba escrita en C, las únicas dependencias externas eran la biblioteca libc y no había ninguna asignación de memoria dinámica. Para reducir el tiempo de procesamiento, el sistema comienza con un conjunto estático de matrices y con la reubicación de datos estáticos: primero, todos los datos del día actual se cargan en la memoria y no se realiza más acceso al disco, todo el trabajo se realiza solo en la memoria. Cuando se inicia el sistema, todos los datos de referencia ya están ordenados, por lo que la búsqueda funciona de manera muy eficiente y requiere poco tiempo en tiempo de ejecución. Todas las tablas están hechas con listas y árboles intrusivos para estructuras de datos dinámicas para que no requieran asignación de memoria en tiempo de ejecución.

Repasemos brevemente la historia del desarrollo de nuestro sistema de negociación y compensación.
La primera versión de la arquitectura del sistema de negociación y compensación se basó en la llamada interacción Unix: se utilizaron memoria compartida, semáforos y colas, y cada proceso constaba de un solo hilo. Este enfoque se generalizó a principios de los años 1990.

La primera versión del sistema contenía dos niveles de Gateway y un servidor central del sistema comercial. El flujo de trabajo fue así:

  • El cliente envía una solicitud que llega al Gateway. Comprueba la validez del formato (pero no los datos en sí) y rechaza transacciones incorrectas.
  • Si se ha enviado una solicitud de información, se ejecuta localmente; si hablamos de una transacción, se redirige al servidor central.
  • Luego, el motor comercial procesa la transacción, modifica la memoria local y envía una respuesta a la transacción y a la transacción misma para su replicación utilizando un motor de replicación independiente.
  • El Gateway recibe la respuesta del nodo central y la reenvía al cliente.
  • Después de un tiempo, el Gateway recibe la transacción a través del mecanismo de replicación, y esta vez la ejecuta localmente, cambiando sus estructuras de datos para que las próximas solicitudes de información muestren los datos más recientes.

De hecho, describe un modelo de replicación en el que Gateway replica completamente las acciones realizadas en el sistema comercial. Un canal de replicación separado garantizaba que las transacciones se ejecutaran en el mismo orden en múltiples nodos de acceso.

Dado que el código era de un solo subproceso, se utilizó un esquema clásico con bifurcaciones de proceso para atender a muchos clientes. Sin embargo, era muy costoso bifurcar toda la base de datos, por lo que se utilizaron procesos de servicio livianos que recolectaron paquetes de sesiones TCP y los transfirieron a una cola (Cola de mensajes SystemV). Gateway y Trade Engine trabajaron solo con esta cola, tomando transacciones desde allí para su ejecución. Ya no era posible enviarle una respuesta porque no estaba claro qué proceso de servicio debía leerlo. Entonces recurrimos a un truco: cada proceso bifurcado creaba una cola de respuestas para sí mismo y, cuando llegaba una solicitud a la cola entrante, se le agregaba inmediatamente una etiqueta para la cola de respuestas.

Copiar constantemente grandes cantidades de datos de una cola a otra creaba problemas, especialmente típicos de las solicitudes de información. Por lo tanto, utilizamos otro truco: además de la cola de respuestas, cada proceso también creaba memoria compartida (SystemV Shared Memory). Los paquetes mismos se colocaron en él y solo se almacenó una etiqueta en la cola, lo que permitió encontrar el paquete original. Esto ayudó a almacenar datos en la memoria caché del procesador.

SystemV IPC incluye utilidades para ver el estado de la cola, la memoria y los objetos de semáforo. Usamos esto activamente para comprender qué estaba sucediendo en el sistema en un momento particular, dónde se acumulaban los paquetes, qué estaba bloqueado, etc.

Primeras actualizaciones

En primer lugar, nos deshicimos del Gateway de proceso único. Su importante inconveniente era que podía manejar una transacción de replicación o una solicitud de información de un cliente. Y a medida que aumenta la carga, Gateway tardará más en procesar las solicitudes y no podrá procesar el flujo de replicación. Además, si el cliente envió una transacción, solo necesita verificar su validez y reenviarla. Por lo tanto, reemplazamos el proceso de puerta de enlace único con múltiples componentes que pueden ejecutarse en paralelo: información de subprocesos múltiples y procesos de transacciones que se ejecutan de forma independiente entre sí en un área de memoria compartida mediante bloqueo RW. Y al mismo tiempo introdujimos procesos de envío y replicación.

Impacto del comercio de alta frecuencia

La versión anterior de la arquitectura existió hasta 2010. Mientras tanto, ya no estábamos satisfechos con el rendimiento de los servidores HP Superdome. Además, la arquitectura PA-RISC estaba prácticamente muerta; el proveedor no ofreció actualizaciones significativas. Como resultado, comenzamos a pasar de HP UX/PA RISC a Linux/x86. La transición comenzó con la adaptación de los servidores de acceso.

¿Por qué tuvimos que cambiar la arquitectura nuevamente? El hecho es que el comercio de alta frecuencia ha cambiado significativamente el perfil de carga en el núcleo del sistema.

Digamos que tenemos una pequeña transacción que provocó un cambio de precio significativo: alguien compró XNUMX millones de dólares. Después de un par de milisegundos, todos los participantes del mercado se dan cuenta y comienzan a realizar una corrección. Naturalmente, las solicitudes se alinean en una cola enorme, que el sistema tardará mucho en eliminar.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

En este intervalo de 50 ms, la velocidad promedio es de aproximadamente 16 mil transacciones por segundo. Si reducimos la ventana a 20 ms, obtenemos una velocidad promedio de 90 mil transacciones por segundo, con 200 mil transacciones en el pico. Es decir, la carga no es constante, con ráfagas repentinas. Y la cola de solicitudes siempre debe procesarse rápidamente.

Pero ¿por qué hay cola? Entonces, en nuestro ejemplo, muchos usuarios notaron el cambio de precio y enviaron transacciones en consecuencia. Llegan a Gateway, este los serializa, establece un orden determinado y los envía a la red. Los enrutadores mezclan los paquetes y los reenvían. Cuyo paquete llegó primero, esa transacción "ganó". Como resultado, los clientes de Exchange comenzaron a notar que si la misma transacción se enviaba desde varios Gateways, aumentaban las posibilidades de su rápido procesamiento. Pronto, los robots de intercambio comenzaron a bombardear Gateway con solicitudes y surgió una avalancha de transacciones.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

Una nueva ronda de evolución

Después de extensas pruebas e investigaciones, cambiamos al kernel del sistema operativo en tiempo real. Para ello elegimos RedHat Enterprise MRG Linux, donde MRG significa mensajería en tiempo real. La ventaja de los parches en tiempo real es que optimizan el sistema para una ejecución lo más rápida posible: todos los procesos se alinean en una cola FIFO, los núcleos se pueden aislar, no hay expulsiones y todas las transacciones se procesan en estricta secuencia.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1
Rojo: trabajando con una cola en un kernel normal, verde: trabajando en un kernel en tiempo real.

Pero lograr una baja latencia en servidores normales no es tan fácil:

  • Interfiere mucho con el modo SMI, que en la arquitectura x86 es la base para trabajar con periféricos importantes. El firmware realiza el procesamiento de todo tipo de eventos de hardware y la gestión de componentes y dispositivos en el llamado modo SMI transparente, en el que el sistema operativo no ve en absoluto lo que está haciendo el firmware. Como regla general, todos los principales proveedores ofrecen extensiones especiales para servidores de firmware que permiten reducir la cantidad de procesamiento SMI.
  • No debe haber control dinámico de la frecuencia del procesador, esto genera un tiempo de inactividad adicional.
  • Cuando se vacía el registro del sistema de archivos, se producen ciertos procesos en el kernel que provocan retrasos impredecibles.
  • Debe prestar atención a cosas como la afinidad de la CPU, la afinidad de interrupción y la NUMA.

Debo decir que el tema de la configuración del hardware y el kernel de Linux para el procesamiento en tiempo real merece un artículo aparte. Pasamos mucho tiempo experimentando e investigando antes de lograr un buen resultado.

Al pasar de servidores PA-RISC a x86, prácticamente no tuvimos que cambiar mucho el código del sistema, simplemente lo adaptamos y reconfiguramos. Al mismo tiempo, solucionamos varios errores. Por ejemplo, las consecuencias del hecho de que PA RISC fuera un sistema Big Endian y x86 fuera un sistema Little Endian surgieron rápidamente: por ejemplo, los datos se leían incorrectamente. El error más complicado fue que PA RISC usa consistentemente consistente (secuencialmente consistente) acceso a la memoria, mientras que x86 puede reordenar las operaciones de lectura, por lo que el código que era absolutamente válido en una plataforma se rompía en otra.

Después de cambiar a x86, el rendimiento casi se triplicó y el tiempo promedio de procesamiento de transacciones disminuyó a 60 μs.

Ahora echemos un vistazo más de cerca a los cambios clave que se han realizado en la arquitectura del sistema.

Épica de reserva caliente

Al cambiar a servidores básicos, nos dimos cuenta de que eran menos fiables. Por lo tanto, al crear una nueva arquitectura, asumimos a priori la posibilidad de falla de uno o más nodos. Por lo tanto, se necesitaba un sistema de reserva activa que pudiera cambiar muy rápidamente a máquinas de respaldo.

Además, había otros requisitos:

  • Bajo ninguna circunstancia debes perder las transacciones procesadas.
  • El sistema debe ser absolutamente transparente para nuestra infraestructura.
  • Los clientes no deberían ver conexiones interrumpidas.
  • Las reservas no deberían introducir retrasos significativos porque este es un factor crítico para el intercambio.

Al crear un sistema de espera activo, no consideramos escenarios similares como fallas dobles (por ejemplo, la red en un servidor dejó de funcionar y el servidor principal se congeló); no consideró la posibilidad de errores en el software porque se identifican durante las pruebas; y no consideró el funcionamiento incorrecto del hardware.

Como resultado, llegamos al siguiente esquema:

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

  • El servidor principal interactuaba directamente con los servidores de puerta de enlace.
  • Todas las transacciones recibidas en el servidor principal se replicaron instantáneamente en el servidor de respaldo a través de un canal separado. El árbitro (gobernador) coordinaba el cambio si surgía algún problema.

    La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

  • El servidor principal procesó cada transacción y esperó la confirmación del servidor de respaldo. Para mantener la latencia al mínimo, evitamos esperar a que se complete la transacción en el servidor de respaldo. Dado que el tiempo que tardaba una transacción en viajar a través de la red era comparable al tiempo de ejecución, no se añadió latencia adicional.
  • Solo pudimos verificar el estado de procesamiento de los servidores principal y de respaldo para la transacción anterior, y se desconocía el estado de procesamiento de la transacción actual. Como todavía estábamos usando procesos de un solo subproceso, esperar una respuesta de Backup habría ralentizado todo el flujo de procesamiento, por lo que hicimos un compromiso razonable: verificamos el resultado de la transacción anterior.

La evolución de la arquitectura del sistema de negociación y compensación de la Bolsa de Moscú. Parte 1

El esquema funcionó de la siguiente manera.

Digamos que el servidor principal deja de responder, pero los Gateways continúan comunicándose. Se agota el tiempo de espera en el servidor de respaldo, este se comunica con el Gobernador, quien le asigna la función del servidor principal, y todos los Gateways cambian al nuevo servidor principal.

Si el servidor principal vuelve a estar en línea, también activa un tiempo de espera interno, porque no ha habido llamadas al servidor desde la puerta de enlace durante un tiempo determinado. Luego también se dirige al gobernador y lo excluye del plan. Como resultado, el intercambio funciona con un servidor hasta el final del período de negociación. Dado que la probabilidad de fallo del servidor es bastante baja, este esquema se consideró bastante aceptable, no contenía una lógica compleja y era fácil de probar.

Esta historia continuará.

Fuente: habr.com

Añadir un comentario