Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Dette er en fortsættelse af en lang historie om vores vanskelige vej til at skabe et kraftfuldt, højbelastningssystem, der sikrer driften af ​​Børsen. Første del er her: habr.com/en/post/444300

Mystisk fejl

Efter adskillige tests blev det opdaterede handels- og clearingsystem sat i drift, og vi stod over for en fejl, som vi kunne skrive en detektiv-mystisk historie om.

Kort efter lancering på hovedserveren blev en af ​​transaktionerne behandlet med en fejl. Alt var dog fint på backup-serveren. Det viste sig, at en simpel matematisk operation med at beregne eksponenten på hovedserveren gav et negativt resultat fra det rigtige argument! Vi fortsatte vores research, og i SSE2-registret fandt vi en forskel på én bit, som er ansvarlig for afrunding, når man arbejder med flydende kommatal.

Vi skrev et simpelt testværktøj til at beregne eksponenten med afrundingsbitsættet. Det viste sig, at der i den version af RedHat Linux, vi brugte, var en fejl i arbejdet med den matematiske funktion, da den skæbnesvangre bit blev indsat. Vi rapporterede dette til RedHat, efter et stykke tid modtog vi en patch fra dem og rullede den ud. Fejlen opstod ikke længere, men det var uklart, hvor denne bit overhovedet kom fra? Funktionen stod for det fesetround fra sproget C. Vi analyserede omhyggeligt vores kode i søgen efter den formodede fejl: vi tjekkede alle mulige situationer; så på alle funktioner, der brugte afrunding; forsøgte at gengive en mislykket session; brugt forskellige compilere med forskellige muligheder; Statisk og dynamisk analyse blev brugt.

Årsagen til fejlen kunne ikke findes.

Så begyndte de at tjekke hardwaren: de udførte belastningstest af processorerne; kontrolleret RAM; Vi kørte endda test for det meget usandsynlige scenarie med en multi-bit fejl i én celle. Til ingen nytte.

Til sidst slog vi os fast på en teori fra højenergifysikkens verden: en eller anden højenergipartikel fløj ind i vores datacenter, gennemborede kabinetvæggen, ramte processoren og fik udløserlåsen til at sætte sig i netop det. Denne absurde teori blev kaldt "neutrinoen". Hvis du er langt fra partikelfysik: neutrinoer interagerer næsten ikke med omverdenen, og er bestemt ikke i stand til at påvirke driften af ​​processoren.

Da det ikke var muligt at finde årsagen til fejlen, blev den "fornærmende" server fjernet fra drift for en sikkerheds skyld.

Efter nogen tid begyndte vi at forbedre det varme backup-system: vi introducerede såkaldte "varme reserver" (varm) - asynkrone replikaer. De modtog en strøm af transaktioner, der kunne være placeret i forskellige datacentre, men warms interagerede ikke aktivt med andre servere.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Hvorfor blev dette gjort? Hvis backupserveren fejler, bliver varm bundet til hovedserveren den nye backup. Det vil sige, efter en fejl, forbliver systemet ikke med én hovedserver, før handelssessionens afslutning.

Og da den nye version af systemet blev testet og sat i drift, opstod afrundingsbitfejlen igen. Desuden begyndte fejlen at dukke op oftere med stigningen i antallet af varme servere. Samtidig havde sælgeren intet at vise, da der ikke var konkrete beviser.

Under den næste analyse af situationen opstod en teori om, at problemet kunne relateres til OS. Vi skrev et simpelt program, der kalder en funktion i en endeløs løkke fesetround, husker den aktuelle tilstand og tjekker den gennem søvn, og dette gøres i mange konkurrerende tråde. Efter at have valgt parametrene for søvn og antallet af tråde, begyndte vi konsekvent at reproducere bitfejlen efter cirka 5 minutters kørsel af værktøjet. Red Hat-support var dog ikke i stand til at gengive det. Test af vores andre servere har vist, at kun dem med bestemte processorer er modtagelige for fejlen. Samtidig løste skift til en ny kerne problemet. Til sidst erstattede vi simpelthen OS, og den sande årsag til fejlen forblev uklar.

Og pludselig sidste år blev der publiceret en artikel på Habré “Hvordan jeg fandt en fejl i Intel Skylake-processorer" Situationen beskrevet i den lignede meget vores, men forfatteren tog undersøgelsen videre og fremsatte en teori om, at fejlen var i mikrokoden. Og når Linux-kerner opdateres, opdaterer producenterne også mikrokoden.

Videreudvikling af systemet

Selvom vi slap af med fejlen, tvang denne historie os til at genoverveje systemarkitekturen. Vi var trods alt ikke beskyttet mod gentagelse af sådanne fejl.

Følgende principper dannede grundlag for de næste forbedringer af reservationssystemet:

  • Du kan ikke stole på nogen. Servere fungerer muligvis ikke korrekt.
  • Flertalsforbehold.
  • Sikring af konsensus. Som en logisk tilføjelse til flertalsforbeholdet.
  • Dobbeltfejl er mulige.
  • Vitalitet. Den nye varme standby-ordning burde ikke være værre end den forrige. Handel bør fortsætte uafbrudt indtil den sidste server.
  • Let stigning i latens. Enhver nedetid medfører store økonomiske tab.
  • Minimal netværksinteraktion for at holde ventetiden så lav som muligt.
  • Valg af en ny masterserver på få sekunder.

Ingen af ​​de tilgængelige løsninger på markedet passede til os, og Raft-protokollen var stadig i sin vorden, så vi skabte vores egen løsning.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Netværk

Ud over reservationssystemet begyndte vi at modernisere netværksinteraktionen. I/O-undersystemet bestod af mange processer, som havde den værste indvirkning på jitter og latens. Med hundredvis af processer, der håndterede TCP-forbindelser, var vi tvunget til konstant at skifte mellem dem, og på mikrosekunders skala er dette en ret tidskrævende operation. Men det værste er, at når en proces modtog en pakke til behandling, sendte den den til én SystemV-kø og ventede derefter på en hændelse fra en anden SystemV-kø. Men når der er et stort antal noder, repræsenterer ankomsten af ​​en ny TCP-pakke i én proces og modtagelse af data i køen i en anden to konkurrerende begivenheder for OS. I dette tilfælde, hvis der ikke er fysiske processorer til rådighed for begge opgaver, vil den ene blive behandlet, og den anden vil blive placeret i en ventekø. Det er umuligt at forudsige konsekvenserne.

I sådanne situationer kan dynamisk procesprioritetsstyring anvendes, men dette vil kræve brug af ressourcekrævende systemkald. Som et resultat skiftede vi til én tråd ved hjælp af klassisk epoll, dette øgede hastigheden markant og reducerede transaktionsbehandlingstiden. Vi slap også af med separate netværkskommunikationsprocesser og kommunikation gennem SystemV, reducerede antallet af systemopkald markant og begyndte at kontrollere prioriteringerne af operationer. Alene på I/O-undersystemet var det muligt at spare omkring 8-17 mikrosekunder, afhængigt af scenariet. Dette enkelttrådede skema er blevet brugt uændret siden da; én epolltråd med en margin er nok til at servicere alle forbindelser.

Transaktionsbehandling

Den voksende belastning på vores system krævede opgradering af næsten alle dets komponenter. Men desværre har stagnation i væksten i processorens clockhastigheder i de senere år ikke længere gjort det muligt at skalere processer direkte. Derfor besluttede vi at opdele Engine-processen i tre niveauer, hvor den travleste af dem er risikokontrolsystemet, som evaluerer tilgængeligheden af ​​midler på konti og selv opretter transaktionerne. Men penge kan være i forskellige valutaer, og det var nødvendigt at finde ud af, på hvilket grundlag behandlingen af ​​anmodninger skulle opdeles.

Den logiske løsning er at opdele det efter valuta: en server handler i dollars, en anden i pund og en tredje i euro. Men hvis der med en sådan ordning sendes to transaktioner for at købe forskellige valutaer, vil problemet med tegnebogsdesynkronisering opstå. Men synkronisering er svært og dyrt. Derfor ville det være korrekt at skære separat med tegnebøger og separat med instrumenter. De fleste vestlige børser har i øvrigt ikke til opgave at kontrollere risici så akut, som vi gør, så oftest foregår dette offline. Vi var nødt til at implementere online verifikation.

Lad os forklare med et eksempel. En erhvervsdrivende ønsker at købe $30, og anmodningen går til transaktionsvalidering: vi kontrollerer, om denne erhvervsdrivende har tilladelse til denne handelstilstand, og om han har de nødvendige rettigheder. Hvis alt er i orden, går anmodningen til risikoverifikationssystemet, dvs. at kontrollere, om der er tilstrækkelige midler til at gennemføre en transaktion. Der er en note om, at det nødvendige beløb i øjeblikket er spærret. Anmodningen videresendes derefter til handelssystemet, som godkender eller afviser transaktionen. Lad os sige, at transaktionen er godkendt - så markerer risikoverifikationssystemet, at pengene er ophævet, og rublerne bliver til dollars.

Generelt indeholder risikokontrolsystemet komplekse algoritmer og udfører en stor mængde meget ressourcekrævende beregninger og kontrollerer ikke blot "kontosaldoen", som det kan se ud ved første øjekast.

Da vi begyndte at opdele Engine-processen i niveauer, stødte vi på et problem: Den kode, der var tilgængelig på det tidspunkt, brugte aktivt den samme række af data på validerings- og verifikationsstadierne, hvilket krævede omskrivning af hele kodebasen. Som et resultat lånte vi en teknik til behandling af instruktioner fra moderne processorer: hver af dem er opdelt i små trin, og flere handlinger udføres parallelt i en cyklus.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Efter en lille tilpasning af koden lavede vi en pipeline til parallel transaktionsbehandling, hvor transaktionen blev opdelt i 4 faser af pipelinen: netværksinteraktion, validering, eksekvering og offentliggørelse af resultatet

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Lad os se på et eksempel. Vi har to behandlingssystemer, seriel og parallel. Den første transaktion ankommer og sendes til validering i begge systemer. Den anden transaktion ankommer med det samme: I et parallelt system tages den straks i arbejde, og i et sekventielt system sættes den i en kø, der venter på, at den første transaktion går igennem den aktuelle behandlingsfase. Det vil sige, at den største fordel ved pipelinebehandling er, at vi behandler transaktionskøen hurtigere.

Sådan kom vi frem til ASTS+ systemet.

Sandt nok er alt heller ikke så glat med transportbånd. Lad os sige, at vi har en transaktion, der påvirker dataarrays i en nabotransaktion; dette er en typisk situation for en udveksling. En sådan transaktion kan ikke udføres i en pipeline, fordi den kan påvirke andre. Denne situation kaldes datahazard, og sådanne transaktioner behandles simpelthen separat: Når de "hurtige" transaktioner i køen løber ud, stopper pipelinen, systemet behandler den "langsomme" transaktion og starter derefter pipelinen igen. Heldigvis er andelen af ​​sådanne transaktioner i det samlede flow meget lille, så pipelinen stopper så sjældent, at det ikke påvirker den samlede præstation.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Så begyndte vi at løse problemet med at synkronisere tre udførelsestråde. Resultatet var et system baseret på en ringbuffer med celler med fast størrelse. I dette system er alt underlagt behandlingshastighed; data kopieres ikke.

  • Alle indkommende netværkspakker går ind i allokeringsfasen.
  • Vi placerer dem i et array og markerer dem som tilgængelige for fase #1.
  • Den anden transaktion er ankommet, den er igen tilgængelig for etape nr. 1.
  • Den første behandlingstråd ser de tilgængelige transaktioner, behandler dem og flytter dem til næste trin i den anden behandlingstråd.
  • Den behandler derefter den første transaktion og markerer den tilsvarende celle deleted — den er nu tilgængelig til ny brug.

Hele køen behandles på denne måde.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Behandling af hvert trin tager enheder eller titusvis af mikrosekunder. Og hvis vi bruger standard OS-synkroniseringsskemaer, så vil vi miste mere tid på selve synkroniseringen. Derfor begyndte vi at bruge spinlock. Dette er dog en meget dårlig form i et realtidssystem, og RedHat anbefaler strengt taget ikke at gøre dette, så vi anvender en spinlock i 100 ms, og skifter derefter til semafortilstand for at eliminere muligheden for en dødvande.

Som et resultat opnåede vi en præstation på omkring 8 millioner transaktioner i sekundet. Og bogstaveligt talt to måneder senere artiklen om LMAX Disruptor så vi en beskrivelse af et kredsløb med samme funktionalitet.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Nu kunne der være flere henrettelsestråde på et trin. Alle transaktioner blev behandlet én efter én, i den rækkefølge, de blev modtaget. Som et resultat steg topydelsen fra 18 til 50 transaktioner i sekundet.

Udvekslingsrisikostyringssystem

Der er ingen grænse for perfektion, og snart begyndte vi moderniseringen igen: Inden for rammerne af ASTS+ begyndte vi at flytte risikostyrings- og afviklingssystemer til autonome komponenter. Vi udviklede en fleksibel moderne arkitektur og en ny hierarkisk risikomodel og forsøgte at bruge klassen, hvor det var muligt fixed_point i stedet for double.

Men der opstod straks et problem: hvordan synkroniseres al den forretningslogik, der har fungeret i mange år, og overfører den til det nye system? Som følge heraf måtte den første version af prototypen af ​​det nye system opgives. Den anden version, som i øjeblikket arbejder i produktion, er baseret på den samme kode, som fungerer i både handels- og risikodelen. Under udviklingen var den sværeste ting at gøre git merge mellem to versioner. Vores kollega Evgeniy Mazurenok udførte denne operation hver uge, og hver gang bandede han i meget lang tid.

Da vi valgte et nyt system, skulle vi straks løse problemet med interaktion. Ved valg af databus var det nødvendigt at sikre stabil jitter og minimal latenstid. InfiniBand RDMA-netværket var bedst egnet til dette: den gennemsnitlige behandlingstid er 4 gange mindre end i 10 G Ethernet-netværk. Men det, der virkelig fangede os, var forskellen i percentiler - 99 og 99,9.

Selvfølgelig har InfiniBand sine udfordringer. For det første en anden API - ibverbs i stedet for sockets. For det andet er der næsten ingen almindeligt tilgængelige open source-meddelelsesløsninger. Vi forsøgte at lave vores egen prototype, men det viste sig at være meget svært, så vi valgte en kommerciel løsning - Confinity Low Latency Messaging (tidligere IBM MQ LLM).

Så opstod opgaven med at opdele risikosystemet korrekt. Hvis du blot fjerner Risk Engine og ikke opretter en mellemnode, kan transaktioner fra to kilder blandes.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

De såkaldte Ultra Low Latency-løsninger har en genbestillingstilstand: transaktioner fra to kilder kan arrangeres i den påkrævede rækkefølge ved modtagelsen; dette implementeres ved hjælp af en separat kanal til udveksling af information om ordren. Men vi bruger endnu ikke denne tilstand: det komplicerer hele processen, og i en række løsninger understøttes det slet ikke. Derudover skal hver transaktion tildeles tilsvarende tidsstempler, og i vores ordning er denne mekanisme meget vanskelig at implementere korrekt. Derfor brugte vi den klassiske ordning med en beskedmægler, altså med en dispatcher, der distribuerer beskeder mellem Risk Engine.

Det andet problem var relateret til klientadgang: hvis der er flere Risk Gateways, skal klienten oprette forbindelse til hver af dem, og dette vil kræve ændringer i klientlaget. Vi ønskede at komme væk fra dette på dette stadium, så det nuværende Risk Gateway-design behandler hele datastrømmen. Dette begrænser i høj grad den maksimale gennemstrømning, men forenkler systemintegration i høj grad.

Duplikering

Vores system bør ikke have et enkelt fejlpunkt, det vil sige, at alle komponenter skal duplikeres, inklusive meddelelsesmægleren. Vi løste dette problem ved hjælp af CLLM-systemet: det indeholder en RCMS-klynge, hvor to dispatchere kan arbejde i master-slave-tilstand, og når den ene fejler, skifter systemet automatisk til den anden.

Arbejder med et backup-datacenter

InfiniBand er optimeret til drift som et lokalt netværk, det vil sige til tilslutning af rackmonteret udstyr, og et InfiniBand netværk kan ikke lægges mellem to geografisk fordelte datacentre. Derfor implementerede vi en bro/dispatcher, som forbinder til beskedlageret via almindelige Ethernet-netværk og videresender alle transaktioner til et andet IB-netværk. Når vi skal migrere fra et datacenter, kan vi vælge, hvilket datacenter vi vil arbejde med nu.

Resultaterne af

Alt ovenstående blev ikke gjort på én gang; det tog flere gentagelser at udvikle en ny arkitektur. Vi skabte prototypen på en måned, men det tog mere end to år at få den til at fungere. Vi forsøgte at opnå det bedste kompromis mellem at øge transaktionsbehandlingstiden og øge systemets pålidelighed.

Da systemet var stærkt opdateret, implementerede vi datagendannelse fra to uafhængige kilder. Hvis beskedlageret af en eller anden grund ikke fungerer korrekt, kan du tage transaktionsloggen fra en anden kilde - fra Risk Engine. Dette princip overholdes i hele systemet.

Blandt andet var vi i stand til at bevare klient-API'en, så hverken mæglere eller andre ville kræve væsentlig omarbejdelse af den nye arkitektur. Vi skulle ændre nogle grænseflader, men der var ingen grund til at lave væsentlige ændringer i driftsmodellen.

Vi kaldte den nuværende version af vores platform Rebus – som en forkortelse for de to mest bemærkelsesværdige innovationer inden for arkitekturen, Risk Engine og BUS.

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

I første omgang ønskede vi kun at tildele clearing-delen, men resultatet blev et enormt distribueret system. Kunder kan nu interagere med enten Trade Gateway, Clearing Gateway eller begge dele.

Hvad vi i sidste ende opnåede:

Udviklingen af ​​arkitekturen i handels- og clearingsystemet på Moskva-børsen. Del 2

Reducerede latensniveauet. Med en lille mængde transaktioner fungerer systemet på samme måde som den tidligere version, men kan samtidig modstå en meget højere belastning.

Peak performance steg fra 50 tusinde til 180 tusinde transaktioner i sekundet. En yderligere stigning hæmmes af den eneste strøm af ordrematchning.

Der er to måder til yderligere forbedring: parallelisering af matchning og ændring af den måde, det fungerer på med Gateway. Nu fungerer alle Gateways i henhold til et replikeringsskema, som under en sådan belastning holder op med at fungere normalt.

Til sidst kan jeg give nogle råd til dem, der er ved at færdiggøre virksomhedssystemer:

  • Vær forberedt på det værste til enhver tid. Problemer opstår altid uventet.
  • Det er normalt umuligt hurtigt at lave arkitektur om. Især hvis du skal opnå maksimal pålidelighed på tværs af flere indikatorer. Jo flere noder, jo flere ressourcer er der brug for til support.
  • Alle tilpassede og proprietære løsninger vil kræve yderligere ressourcer til forskning, support og vedligeholdelse.
  • Udsæt ikke løsning af problemer med systempålidelighed og gendannelse efter fejl; tag dem i betragtning i den indledende designfase.

Kilde: www.habr.com

Tilføj en kommentar