Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Salutare tuturor! Numele meu este Sergey Kostanbaev, la Bursă dezvolt nucleul sistemului de tranzacționare.

Când filmele de la Hollywood arată Bursa de Valori din New York, întotdeauna arată așa: mulțimi de oameni, toată lumea strigă ceva, fluturează hârtiile, se întâmplă un haos total. Acest lucru nu s-a întâmplat niciodată aici la Bursa din Moscova, deoarece tranzacționarea s-a desfășurat electronic de la bun început și se bazează pe două platforme principale - Spectra (piața valutară) și ASTS (piața valutară, bursiera și monetară). Și astăzi vreau să vorbesc despre evoluția arhitecturii sistemului de tranzacționare și compensare ASTS, despre diverse soluții și constatări. Povestea va fi lungă, așa că a trebuit să o despart în două părți.

Suntem una dintre puținele burse din lume care tranzacționează active de toate clasele și oferă o gamă completă de servicii de schimb. De exemplu, anul trecut ne-am clasat pe locul al doilea în lume în ceea ce privește volumul tranzacționării obligațiunilor, locul 25 între toate bursele, locul 13 la capitalizare în rândul burselor publice.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Pentru participanții profesioniști la tranzacționare, parametri precum timpul de răspuns, stabilitatea distribuției timpului (jitter) și fiabilitatea întregului complex sunt critici. În prezent procesăm zeci de milioane de tranzacții pe zi. Procesarea fiecărei tranzacții de către nucleul de sistem durează zeci de microsecunde. Desigur, operatorii de telefonie mobilă de Revelion sau motoarele de căutare în sine au un volum de muncă mai mare decât al nostru, dar în ceea ce privește volumul de muncă, cuplat cu caracteristicile mai sus menționate, puțini se pot compara cu noi, mi se pare. În același timp, este important pentru noi ca sistemul să nu încetinească nicio secundă, să funcționeze absolut stabil și toți utilizatorii să fie pe picior de egalitate.

Puțină istorie

În 1994, sistemul australian ASTS a fost lansat la Moscow Interbank Currency Exchange (MICEX), iar din acel moment se poate număra istoria rusă a tranzacționării electronice. În 1998, arhitectura bursei a fost modernizată pentru a introduce tranzacționarea pe internet. De atunci, viteza de implementare a noilor soluții și modificări arhitecturale în toate sistemele și subsistemele a câștigat avânt.

În acei ani, sistemul de schimb a funcționat pe hardware de vârf - servere HP Superdome 9000 ultra-fiabile (construite pe PA-RISC), în care absolut totul a fost duplicat: subsisteme de intrare/ieșire, rețea, RAM (de fapt, exista o matrice RAID de RAM), procesoare (hot-swappable). Era posibil să se schimbe orice componentă a serverului fără a opri mașina. Ne-am bazat pe aceste dispozitive și le-am considerat practic sigure. Sistemul de operare era un sistem HP UX asemănător Unix.

Dar din aproximativ 2010, a apărut un fenomen numit tranzacționare de înaltă frecvență (HFT) sau tranzacționare de înaltă frecvență - pur și simplu, roboți bursieri. În doar 2,5 ani, sarcina pe serverele noastre a crescut de 140 de ori.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Era imposibil să reziste la o astfel de încărcare cu arhitectura și echipamentele vechi. Era necesar să ne adaptăm cumva.

Home

Cererile către sistemul de schimb pot fi împărțite în două tipuri:

  • Tranzacții. Dacă doriți să cumpărați dolari, acțiuni sau altceva, trimiteți o tranzacție către sistemul de tranzacționare și primiți un răspuns despre succes.
  • Solicitări de informații. Dacă doriți să aflați prețul curent, vizualizați carnetul de comenzi sau indici, apoi trimiteți cereri de informații.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Schematic, nucleul sistemului poate fi împărțit în trei niveluri:

  • Nivelul clientului, la care lucrează brokerii și clienții. Toți interacționează cu serverele de acces.
  • Serverele gateway sunt servere de stocare în cache care procesează local toate solicitările de informații. Doriți să știți la ce preț se tranzacționează în prezent acțiunile Sberbank? Solicitarea ajunge la serverul de acces.
  • Dar dacă doriți să cumpărați acțiuni, atunci cererea se duce la serverul central (Trade Engine). Există câte un astfel de server pentru fiecare tip de piață, ei joacă un rol vital, tocmai pentru ei am creat acest sistem.

Miezul sistemului de tranzacționare este o bază de date inteligentă în memorie, în care toate tranzacțiile sunt tranzacții de schimb. Baza a fost scrisă în C, singurele dependențe externe au fost biblioteca libc și nu a existat deloc o alocare dinamică de memorie. Pentru a reduce timpul de procesare, sistemul începe cu un set static de matrice și cu relocarea statică a datelor: mai întâi, toate datele pentru ziua curentă sunt încărcate în memorie și nu se mai efectuează acces la disc, toate lucrările sunt efectuate numai în memorie. Când sistemul pornește, toate datele de referință sunt deja sortate, astfel încât căutarea funcționează foarte eficient și durează puțin timp în timpul de execuție. Toate tabelele sunt realizate cu liste intruzive și arbori pentru structuri de date dinamice, astfel încât să nu necesite alocarea de memorie în timpul execuției.

Să trecem pe scurt peste istoria dezvoltării sistemului nostru de tranzacționare și compensare.
Prima versiune a arhitecturii sistemului de tranzacționare și compensare a fost construită pe așa-numita interacțiune Unix: au fost folosite memorie partajată, semafoare și cozi, iar fiecare proces a constat dintr-un singur fir. Această abordare a fost răspândită la începutul anilor 1990.

Prima versiune a sistemului conținea două niveluri de Gateway și un server central al sistemului de tranzacționare. Fluxul de lucru a fost astfel:

  • Clientul trimite o cerere, care ajunge la Gateway. Verifică validitatea formatului (dar nu și datele în sine) și respinge tranzacțiile incorecte.
  • Dacă a fost trimisă o solicitare de informare, aceasta este executată local; dacă vorbim despre o tranzacție, atunci aceasta este redirecționată către serverul central.
  • Motorul de tranzacționare procesează apoi tranzacția, modifică memoria locală și trimite un răspuns la tranzacție și tranzacția în sine pentru replicare folosind un motor de replicare separat.
  • Gateway-ul primește răspunsul de la nodul central și îl transmite către client.
  • După un timp, Gateway-ul primește tranzacția prin mecanismul de replicare, iar de data aceasta o execută local, modificându-și structurile de date astfel încât următoarele solicitări de informații să afișeze cele mai recente date.

De fapt, descrie un model de replicare în care Gateway-ul a replicat complet acțiunile efectuate în sistemul de tranzacționare. Un canal de replicare separat a asigurat că tranzacțiile au fost executate în aceeași ordine pe mai multe noduri de acces.

Deoarece codul era cu un singur thread, a fost folosită o schemă clasică cu furci de proces pentru a servi mulți clienți. Cu toate acestea, a fost foarte costisitor să bifurcați întreaga bază de date, așa că au fost utilizate procese de serviciu ușoare care colectau pachete din sesiunile TCP și le transferau într-o singură coadă (Coada de mesaje SystemV). Gateway și Trade Engine au funcționat numai cu această coadă, preluând tranzacțiile de acolo pentru execuție. Nu a mai fost posibil să se trimită un răspuns la acesta, deoarece nu era clar ce proces de serviciu ar trebui să-l citească. Așa că am recurs la un truc: fiecare proces bifurcat a creat o coadă de răspuns pentru el însuși, iar când o solicitare a intrat în coada de primire, o etichetă pentru coada de răspuns i-a fost adăugată imediat.

Copierea constantă a unor cantități mari de date de la coadă la coadă a creat probleme, mai ales tipice pentru solicitările de informații. Prin urmare, am folosit un alt truc: pe lângă coada de răspunsuri, fiecare proces a creat și memorie partajată (SystemV Shared Memory). Pachetele în sine au fost plasate în el și doar o etichetă a fost stocată în coadă, permițând cuiva să găsească pachetul original. Acest lucru a ajutat la stocarea datelor în memoria cache a procesorului.

SystemV IPC include utilitare pentru vizualizarea stării de coadă, memorie și obiecte semafor. Am folosit acest lucru în mod activ pentru a înțelege ce se întâmplă în sistem la un anumit moment, unde s-au acumulat pachetele, ce a fost blocat etc.

Primele upgrade-uri

În primul rând, am scăpat de Gateway-ul cu un singur proces. Dezavantajul său semnificativ era că putea gestiona fie o tranzacție de replicare, fie o solicitare de informații de la un client. Și pe măsură ce sarcina crește, Gateway-ul va dura mai mult pentru a procesa cererile și nu va putea procesa fluxul de replicare. În plus, dacă clientul a trimis o tranzacție, atunci trebuie doar să verificați valabilitatea acesteia și să o transmiteți în continuare. Prin urmare, am înlocuit un singur proces Gateway cu mai multe componente care pot rula în paralel: informații cu mai multe fire și procese de tranzacție care rulează independent unele de altele pe o zonă de memorie partajată folosind blocarea RW. Și, în același timp, am introdus procese de expediere și replicare.

Impactul tranzacționării de înaltă frecvență

Versiunea de mai sus a arhitecturii a existat până în 2010. Între timp, nu mai eram mulțumiți de performanța serverelor HP Superdome. În plus, arhitectura PA-RISC era practic moartă; furnizorul nu a oferit actualizări semnificative. Ca rezultat, am început să trecem de la HP UX/PA RISC la Linux/x86. Tranziția a început cu adaptarea serverelor de acces.

De ce a trebuit să schimbăm din nou arhitectura? Faptul este că tranzacționarea de înaltă frecvență a schimbat în mod semnificativ profilul de încărcare pe nucleul sistemului.

Să presupunem că avem o tranzacție mică care a provocat o schimbare semnificativă a prețului - cineva a cumpărat jumătate de miliard de dolari. După câteva milisecunde, toți participanții la piață observă acest lucru și încep să facă o corecție. Desigur, cererile se aliniază într-o coadă uriașă, pe care sistemul va dura mult timp pentru a o șterge.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

La acest interval de 50 ms, viteza medie este de aproximativ 16 mii de tranzacții pe secundă. Dacă reducem fereastra la 20 ms, obținem o viteză medie de 90 de mii de tranzacții pe secundă, cu 200 de mii de tranzacții la vârf. Cu alte cuvinte, sarcina nu este constantă, cu explozii bruște. Iar coada de cereri trebuie întotdeauna procesată rapid.

Dar de ce este o coadă? Deci, în exemplul nostru, mulți utilizatori au observat modificarea prețului și au trimis tranzacții în consecință. Ei vin la Gateway, le serializează, stabilește o anumită ordine și le trimite în rețea. Routerele amestecă pachetele și le transmit mai departe. Al cărui pachet a sosit primul, acea tranzacție „a câștigat”. Ca urmare, clienții de schimb au început să observe că dacă aceeași tranzacție a fost trimisă de la mai multe Gateway-uri, atunci șansele procesării rapide ale acesteia au crescut. În curând, roboții de schimb au început să bombardeze Gateway cu cereri și a apărut o avalanșă de tranzacții.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

O nouă rundă de evoluție

După teste și cercetări ample, am trecut la nucleul sistemului de operare în timp real. Pentru aceasta am ales RedHat Enterprise MRG Linux, unde MRG înseamnă grilă de mesagerie în timp real. Avantajul patch-urilor în timp real este că optimizează sistemul pentru cea mai rapidă execuție posibilă: toate procesele sunt aliniate într-o coadă FIFO, nucleele pot fi izolate, fără ejecții, toate tranzacțiile sunt procesate într-o secvență strictă.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1
Roșu - lucrează cu o coadă într-un nucleu obișnuit, verde - lucrează într-un nucleu în timp real.

Dar obținerea unei latențe scăzute pe serverele obișnuite nu este atât de ușoară:

  • Modul SMI, care în arhitectura x86 este baza pentru lucrul cu periferice importante, interferează foarte mult. Procesarea tuturor tipurilor de evenimente hardware și gestionarea componentelor și dispozitivelor este efectuată de firmware în așa-numitul mod transparent SMI, în care sistemul de operare nu vede deloc ce face firmware-ul. De regulă, toți furnizorii importanți oferă extensii speciale pentru serverele de firmware care permit reducerea cantității de procesare SMI.
  • Nu ar trebui să existe un control dinamic al frecvenței procesorului, ceea ce duce la un timp de nefuncționare suplimentar.
  • Când jurnalul sistemului de fișiere este șters, în nucleu apar anumite procese care cauzează întârzieri imprevizibile.
  • Trebuie să acordați atenție lucrurilor precum CPU Affinity, Interrupt afinity, NUMA.

Trebuie să spun că subiectul instalării hardware-ului și nucleului Linux pentru procesarea în timp real merită un articol separat. Am petrecut mult timp experimentând și cercetând înainte de a obține un rezultat bun.

Când am trecut de la serverele PA-RISC la x86, practic nu a trebuit să schimbăm prea mult codul de sistem, doar l-am adaptat și reconfigurat. În același timp, am remediat mai multe erori. De exemplu, consecințele faptului că PA RISC era un sistem Big endian, iar x86 era un sistem Little endian, au apărut rapid: de exemplu, datele au fost citite incorect. Problema mai complicată a fost cea pe care o folosește PA RISC constant consistent (Consecvențial consistent) acces la memorie, în timp ce x86 poate reordona operațiunile de citire, astfel încât codul care era absolut valabil pe o platformă a fost spart pe alta.

După trecerea la x86, performanța a crescut de aproape trei ori, timpul mediu de procesare a tranzacției a scăzut la 60 μs.

Să aruncăm o privire mai atentă asupra modificărilor cheie aduse arhitecturii sistemului.

Epopee de rezervă fierbinte

Când am trecut la serverele de mărfuri, am fost conștienți că acestea erau mai puțin fiabile. Prin urmare, la crearea unei noi arhitecturi, a priori ne-am asumat posibilitatea defecțiunii unuia sau mai multor noduri. Prin urmare, era necesar un sistem de așteptare la cald care să poată trece foarte rapid la mașini de rezervă.

În plus, existau și alte cerințe:

  • În niciun caz nu trebuie să pierdeți tranzacțiile procesate.
  • Sistemul trebuie să fie absolut transparent pentru infrastructura noastră.
  • Clienții nu ar trebui să vadă conexiunile întrerupte.
  • Rezervările nu ar trebui să introducă întârzieri semnificative, deoarece acesta este un factor critic pentru schimb.

La crearea unui sistem de așteptare la cald, nu am considerat astfel de scenarii drept eșecuri duble (de exemplu, rețeaua de pe un server a încetat să funcționeze și serverul principal a înghețat); nu a luat în considerare posibilitatea apariției erorilor în software deoarece acestea sunt identificate în timpul testării; și nu a luat în considerare funcționarea incorectă a hardware-ului.

Ca urmare, am ajuns la următoarea schemă:

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

  • Serverul principal a interacționat direct cu serverele Gateway.
  • Toate tranzacțiile primite pe serverul principal au fost replicate instantaneu pe serverul de rezervă printr-un canal separat. Arbitrul (guvernatorul) a coordonat schimbarea dacă au apărut probleme.

    Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

  • Serverul principal a procesat fiecare tranzacție și a așteptat confirmarea de la serverul de rezervă. Pentru a menține latența la minimum, am evitat să așteptăm finalizarea tranzacției pe serverul de rezervă. Deoarece timpul necesar pentru ca o tranzacție să parcurgă rețea a fost comparabil cu timpul de execuție, nu a fost adăugată nicio latență suplimentară.
  • Am putut verifica doar starea de procesare a serverelor principale și de rezervă pentru tranzacția anterioară, iar starea de procesare a tranzacției curente era necunoscută. Deoarece încă folosim procese cu un singur thread, așteptarea unui răspuns de la Backup ar fi încetinit întregul flux de procesare, așa că am făcut un compromis rezonabil: am verificat rezultatul tranzacției anterioare.

Evoluția arhitecturii sistemului de tranzacționare și compensare a Bursei din Moscova. Partea 1

Schema a funcționat după cum urmează.

Să presupunem că serverul principal nu mai răspunde, dar Gateway-urile continuă să comunice. Are loc un timeout pe serverul de rezervă, acesta contactează Guvernatorul, care îi atribuie rolul de server principal și toate Gateway-urile trec la noul server principal.

Dacă serverul principal revine online, declanșează și un timeout intern, deoarece nu au existat apeluri către server de la Gateway pentru un anumit timp. Apoi se îndreaptă și la Guvernator și îl exclude din schemă. Ca rezultat, schimbul funcționează cu un singur server până la sfârșitul perioadei de tranzacționare. Deoarece probabilitatea unei defecțiuni a serverului este destul de scăzută, această schemă a fost considerată destul de acceptabilă; nu conținea o logică complexă și era ușor de testat.

Pentru a fi continuat.

Sursa: www.habr.com

Adauga un comentariu