L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Salut tout le monde! Je m'appelle Sergey Kostanbaev, à la Bourse, je développe le cœur du système commercial.

Lorsque les films hollywoodiens montrent la Bourse de New York, cela ressemble toujours à ceci : des foules de gens, tout le monde crie quelque chose, agite des papiers, c'est un chaos complet. Cela ne s'est jamais produit ici à la Bourse de Moscou, car les négociations se sont déroulées électroniquement dès le début et sont basées sur deux plates-formes principales - Spectra (marché des changes) et ASTS (marché des changes, boursier et monétaire). Et aujourd'hui, je veux parler de l'évolution de l'architecture du système de négociation et de compensation ASTS, de diverses solutions et découvertes. L'histoire sera longue, j'ai donc dû la diviser en deux parties.

Nous sommes l'une des rares bourses au monde à négocier des actifs de toutes classes et à proposer une gamme complète de services de bourse. Par exemple, l'année dernière, nous nous sommes classés au deuxième rang mondial en termes de volume de transactions obligataires, au 25e rang parmi toutes les bourses et au 13e rang en termes de capitalisation parmi les bourses publiques.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Pour les participants professionnels au trading, des paramètres tels que le temps de réponse, la stabilité de la distribution temporelle (jitter) et la fiabilité de l'ensemble du complexe sont essentiels. Nous traitons actuellement des dizaines de millions de transactions par jour. Le traitement de chaque transaction par le noyau du système prend des dizaines de microsecondes. Bien sûr, les opérateurs mobiles le soir du Nouvel An ou les moteurs de recherche eux-mêmes ont une charge de travail plus élevée que la nôtre, mais en termes de charge de travail, couplée aux caractéristiques mentionnées ci-dessus, peu peuvent se comparer à nous, me semble-t-il. Dans le même temps, il est important pour nous que le système ne ralentisse pas une seconde, qu'il fonctionne de manière absolument stable et que tous les utilisateurs soient sur un pied d'égalité.

Un peu d'histoire

En 1994, le système australien ASTS a été lancé sur le marché interbancaire de change de Moscou (MICEX), et à partir de ce moment, l'histoire russe du commerce électronique peut être comptée. En 1998, l’architecture boursière a été modernisée pour introduire le trading sur Internet. Depuis lors, la vitesse de mise en œuvre de nouvelles solutions et de changements architecturaux dans tous les systèmes et sous-systèmes n'a fait que prendre de l'ampleur.

Au cours de ces années, le système d'échange fonctionnait sur du matériel haut de gamme - des serveurs HP Superdome 9000 ultra-fiables (construits sur le PA-RISC), dans lequel absolument tout était dupliqué : sous-systèmes d'entrée/sortie, réseau, RAM (en fait, il existait une matrice RAID de RAM), processeurs (échangeables à chaud). Il était possible de modifier n'importe quel composant du serveur sans arrêter la machine. Nous nous sommes appuyés sur ces appareils et les avons considérés comme pratiquement infaillibles. Le système d'exploitation était un système HP UX de type Unix.

Mais depuis 2010 environ, un phénomène est apparu appelé trading à haute fréquence (HFT), ou trading à haute fréquence – en termes simples, robots boursiers. En seulement 2,5 ans, la charge sur nos serveurs a été multipliée par 140.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Il était impossible de supporter une telle charge avec l’ancienne architecture et l’équipement. Il fallait en quelque sorte s'adapter.

début

Les demandes adressées au système d'échange peuvent être divisées en deux types :

  • Transactions. Si vous souhaitez acheter des dollars, des actions ou autre chose, vous envoyez une transaction au système commercial et recevez une réponse concernant le succès.
  • Demandes d'informations. Si vous souhaitez connaître le prix actuel, consulter le carnet d'ordres ou les indices, puis envoyer des demandes d'informations.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Schématiquement, le cœur du système peut être divisé en trois niveaux :

  • Le niveau client, auquel travaillent les courtiers et les clients. Ils interagissent tous avec les serveurs d'accès.
  • Les serveurs de passerelle sont des serveurs de mise en cache qui traitent localement toutes les demandes d'informations. Voulez-vous savoir à quel prix les actions de la Sberbank se négocient actuellement ? La requête est envoyée au serveur d'accès.
  • Mais si vous souhaitez acheter des actions, la demande est adressée au serveur central (Trade Engine). Il existe un tel serveur pour chaque type de marché, ils jouent un rôle primordial, c'est pour eux que nous avons créé ce système.

Le cœur du système commercial est une base de données intelligente en mémoire dans laquelle toutes les transactions sont des transactions d'échange. La base a été écrite en C, les seules dépendances externes étaient la bibliothèque libc et il n'y avait aucune allocation de mémoire dynamique. Pour réduire le temps de traitement, le système commence par un ensemble statique de tableaux et par une relocalisation statique des données : d'abord, toutes les données du jour en cours sont chargées en mémoire et aucun autre accès au disque n'est effectué, tout le travail est effectué uniquement en mémoire. Lorsque le système démarre, toutes les données de référence sont déjà triées, la recherche fonctionne donc très efficacement et prend peu de temps d'exécution. Toutes les tables sont créées avec des listes et des arbres intrusifs pour les structures de données dynamiques afin qu'elles ne nécessitent pas d'allocation de mémoire au moment de l'exécution.

Passons brièvement en revue l'histoire du développement de notre système de négociation et de compensation.
La première version de l'architecture du système de négociation et de compensation était construite sur ce qu'on appelle l'interaction Unix : une mémoire partagée, des sémaphores et des files d'attente étaient utilisés, et chaque processus consistait en un seul thread. Cette approche était répandue au début des années 1990.

La première version du système contenait deux niveaux de passerelle et un serveur central du système commercial. Le flux de travail était le suivant :

  • Le client envoie une requête qui atteint la passerelle. Il vérifie la validité du format (mais pas les données elles-mêmes) et rejette les transactions incorrectes.
  • Si une demande d'information a été envoyée, elle est exécutée localement ; si nous parlons d'une transaction, alors elle est redirigée vers le serveur central.
  • Le moteur commercial traite ensuite la transaction, modifie la mémoire locale et envoie une réponse à la transaction et à la transaction elle-même pour réplication à l'aide d'un moteur de réplication distinct.
  • La passerelle reçoit la réponse du nœud central et la transmet au client.
  • Après un certain temps, la passerelle reçoit la transaction via le mécanisme de réplication, et cette fois elle l'exécute localement, en modifiant ses structures de données afin que les prochaines demandes d'informations affichent les dernières données.

En fait, il décrit un modèle de réplication dans lequel la passerelle répliquait complètement les actions effectuées dans le système commercial. Un canal de réplication distinct garantissait que les transactions étaient exécutées dans le même ordre sur plusieurs nœuds d'accès.

Étant donné que le code était monothread, un schéma classique avec des forks de processus a été utilisé pour servir de nombreux clients. Cependant, il était très coûteux de créer la totalité de la base de données, c'est pourquoi des processus de service légers ont été utilisés pour collecter les paquets des sessions TCP et les transférer vers une file d'attente (SystemV Message Queue). Gateway et Trade Engine fonctionnaient uniquement avec cette file d'attente, prenant les transactions à partir de là pour exécution. Il n'était plus possible d'envoyer une réponse, car il n'était pas clair quel processus de service devait la lire. Nous avons donc eu recours à une astuce : chaque processus forké créait une file d'attente de réponses pour lui-même, et lorsqu'une requête arrivait dans la file d'attente entrante, une balise pour la file d'attente de réponses y était immédiatement ajoutée.

La copie constante de grandes quantités de données d'une file d'attente à l'autre créait des problèmes, particulièrement typiques pour les demandes d'informations. Par conséquent, nous avons utilisé une autre astuce : en plus de la file d'attente de réponses, chaque processus a également créé de la mémoire partagée (SystemV Shared Memory). Les packages eux-mêmes y étaient placés et seule une balise était stockée dans la file d'attente, permettant de retrouver le package d'origine. Cela a permis de stocker les données dans le cache du processeur.

SystemV IPC comprend des utilitaires permettant d'afficher l'état des objets de file d'attente, de mémoire et de sémaphore. Nous l'avons activement utilisé pour comprendre ce qui se passait dans le système à un moment donné, où les paquets s'accumulaient, ce qui était bloqué, etc.

Premières mises à jour

Tout d’abord, nous nous sommes débarrassés de la passerelle à processus unique. Son inconvénient majeur était qu'il pouvait gérer soit une transaction de réplication, soit une demande d'informations d'un client. Et à mesure que la charge augmente, Gateway mettra plus de temps à traiter les demandes et ne sera pas en mesure de traiter le flux de réplication. De plus, si le client a envoyé une transaction, il vous suffit alors de vérifier sa validité et de la transmettre davantage. Par conséquent, nous avons remplacé le processus de passerelle unique par plusieurs composants pouvant s'exécuter en parallèle : des processus d'information et de transaction multithread s'exécutant indépendamment les uns des autres sur une zone de mémoire partagée à l'aide du verrouillage RW. Et en même temps, nous avons introduit des processus de répartition et de réplication.

Impact du trading haute fréquence

La version ci-dessus de l'architecture existait jusqu'en 2010. Pendant ce temps, nous n'étions plus satisfaits des performances des serveurs HP Superdome. De plus, l'architecture PA-RISC était pratiquement morte ; l'éditeur ne proposait aucune mise à jour significative. En conséquence, nous avons commencé à passer de HP UX/PA RISC à Linux/x86. La transition a commencé avec l'adaptation des serveurs d'accès.

Pourquoi avons-nous dû changer à nouveau l’architecture ? Le fait est que le trading à haute fréquence a considérablement modifié le profil de charge sur le cœur du système.

Disons que nous avons une petite transaction qui a provoqué un changement de prix important : quelqu'un a acheté un demi-milliard de dollars. Après quelques millisecondes, tous les acteurs du marché le remarquent et commencent à effectuer une correction. Naturellement, les demandes s'alignent dans une énorme file d'attente, que le système mettra beaucoup de temps à effacer.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

À cet intervalle de 50 ms, la vitesse moyenne est d'environ 16 20 transactions par seconde. Si nous réduisons la fenêtre à 90 ms, nous obtenons une vitesse moyenne de 200 XNUMX transactions par seconde, avec XNUMX XNUMX transactions au maximum. Autrement dit, la charge n’est pas constante, avec des éclatements brusques. Et la file d’attente des demandes doit toujours être traitée rapidement.

Mais pourquoi y a-t-il une file d’attente ? Ainsi, dans notre exemple, de nombreux utilisateurs ont remarqué le changement de prix et ont envoyé des transactions en conséquence. Ils arrivent sur Gateway, il les sérialise, définit un certain ordre et les envoie au réseau. Les routeurs mélangent les paquets et les transmettent. Dont le colis est arrivé en premier, cette transaction « a gagné ». En conséquence, les clients d'échange ont commencé à remarquer que si la même transaction était envoyée depuis plusieurs passerelles, les chances de son traitement rapide augmentaient. Bientôt, les robots d’échange ont commencé à bombarder Gateway de requêtes, et une avalanche de transactions s’est produite.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Un nouveau cycle d'évolution

Après des tests et des recherches approfondis, nous sommes passés au noyau du système d'exploitation en temps réel. Pour cela, nous avons choisi RedHat Enterprise MRG Linux, où MRG signifie messagerie en temps réel. L'avantage des correctifs en temps réel est qu'ils optimisent le système pour une exécution la plus rapide possible : tous les processus sont alignés dans une file d'attente FIFO, les cœurs peuvent être isolés, pas d'éjections, toutes les transactions sont traitées dans un ordre strict.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1
Rouge - travailler avec une file d'attente dans un noyau standard, vert - travailler dans un noyau temps réel.

Mais obtenir une faible latence sur des serveurs classiques n’est pas si simple :

  • Le mode SMI, qui dans l'architecture x86 constitue la base du travail avec des périphériques importants, interfère grandement. Le traitement de toutes sortes d'événements matériels et la gestion des composants et des appareils sont effectués par le micrologiciel dans ce que l'on appelle le mode SMI transparent, dans lequel le système d'exploitation ne voit pas du tout ce que fait le micrologiciel. En règle générale, tous les principaux fournisseurs proposent des extensions spéciales pour les serveurs de micrologiciels qui permettent de réduire la quantité de traitement SMI.
  • Il ne devrait y avoir aucun contrôle dynamique de la fréquence du processeur, cela entraînerait des temps d'arrêt supplémentaires.
  • Lorsque le journal du système de fichiers est vidé, certains processus se produisent dans le noyau et entraînent des retards imprévisibles.
  • Vous devez faire attention à des éléments tels que l'affinité CPU, l'affinité d'interruption, NUMA.

Je dois dire que le sujet de la configuration du matériel et du noyau Linux pour le traitement en temps réel mérite un article séparé. Nous avons passé beaucoup de temps à expérimenter et à faire des recherches avant d'obtenir un bon résultat.

Lors du passage des serveurs PA-RISC aux x86, nous n'avons pratiquement pas eu à changer beaucoup le code système, nous l'avons simplement adapté et reconfiguré. En parallèle, nous avons corrigé plusieurs bugs. Par exemple, les conséquences du fait que PA RISC était un système Big Endian et x86 était un système Little Endian, sont rapidement apparues : par exemple, les données ont été lues de manière incorrecte. Le bug le plus délicat était que PA RISC utilise toujours cohérent (Séquentiellement cohérent) accès à la mémoire, alors que x86 peut réorganiser les opérations de lecture, de sorte que le code qui était absolument valide sur une plate-forme est devenu cassé sur une autre.

Après le passage à x86, les performances ont presque triplé et le temps moyen de traitement des transactions a diminué à 60 μs.

Examinons maintenant de plus près les principaux changements qui ont été apportés à l'architecture du système.

Épique de réserve chaude

En passant aux serveurs standards, nous étions conscients qu’ils étaient moins fiables. Par conséquent, lors de la création d’une nouvelle architecture, nous avons supposé a priori la possibilité de défaillance d’un ou plusieurs nœuds. Il fallait donc un système de secours automatique, capable de passer très rapidement à des machines de secours.

De plus, il y avait d'autres exigences :

  • En aucun cas vous ne devez perdre les transactions traitées.
  • Le système doit être absolument transparent pour notre infrastructure.
  • Les clients ne devraient pas voir les connexions interrompues.
  • Les réservations ne devraient pas introduire de retard significatif car il s’agit d’un facteur critique pour l’échange.

Lors de la création d'un système de secours chaud, nous n'avons pas pris en compte des scénarios tels que des doubles pannes (par exemple, le réseau d'un serveur a cessé de fonctionner et le serveur principal s'est figé) ; n'a pas envisagé la possibilité d'erreurs dans le logiciel car elles sont identifiées lors des tests ; et n'a pas pris en compte le mauvais fonctionnement du matériel.

En conséquence, nous sommes arrivés au schéma suivant :

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

  • Le serveur principal interagissait directement avec les serveurs Gateway.
  • Toutes les transactions reçues sur le serveur principal étaient instantanément répliquées sur le serveur de sauvegarde via un canal distinct. L'arbitre (le gouverneur) coordonnait le changement en cas de problème.

    L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

  • Le serveur principal traitait chaque transaction et attendait la confirmation du serveur de sauvegarde. Pour minimiser la latence, nous avons évité d'attendre la fin de la transaction sur le serveur de sauvegarde. Étant donné que le temps nécessaire à une transaction pour parcourir le réseau était comparable au temps d’exécution, aucune latence supplémentaire n’a été ajoutée.
  • Nous ne pouvions vérifier que l'état de traitement des serveurs principal et de sauvegarde pour la transaction précédente, et l'état de traitement de la transaction en cours était inconnu. Comme nous utilisions encore des processus monothread, attendre une réponse de Backup aurait ralenti tout le flux de traitement, nous avons donc fait un compromis raisonnable : nous avons vérifié le résultat de la transaction précédente.

L'évolution de l'architecture du système de négociation et de compensation de la Bourse de Moscou. Partie 1

Le schéma fonctionnait comme suit.

Disons que le serveur principal ne répond plus, mais que les passerelles continuent de communiquer. Un timeout se produit sur le serveur de sauvegarde, celui-ci contacte le Gouverneur, qui lui attribue le rôle de serveur principal, et toutes les passerelles basculent vers le nouveau serveur principal.

Si le serveur principal revient en ligne, cela déclenche également un timeout interne, car il n'y a eu aucun appel vers le serveur depuis la passerelle depuis un certain temps. Puis il se tourne également vers le gouverneur, et celui-ci l'exclut du projet. En conséquence, la bourse fonctionne avec un seul serveur jusqu'à la fin de la période de négociation. Étant donné que la probabilité d'une panne de serveur est assez faible, ce schéma a été considéré comme tout à fait acceptable : il ne contenait pas de logique complexe et était facile à tester.

A suivre.

Source: habr.com

Ajouter un commentaire