Avvisu
I culleghi, à a mità di l'estiu, aghju pensatu à liberà una altra serie d'articuli nantu à u disignu di sistemi di fila: "L'esperimentu VTrade" - un tentativu di scrive un framework per i sistemi di cummerciale. A serie esaminerà a teoria è a pratica di custruisce un scambiu, l'asta è a tenda. À a fine di l'articulu, vi invitu à votà per i temi chì vi interessanu di più.
Questu hè l'articulu finale in a serie nantu à l'applicazioni reattive distribuite in Erlang/Elixir. IN
Oghje avemu da suscitarà prublemi di sviluppu di a basa di codice è prughjetti in generale.
Urganisazione di servizii
In a vita reale, quandu si sviluppa un serviziu, spessu avete da cumminà parechji mudelli di interazzione in un controller. Per esempiu, u serviziu di l'utilizatori, chì risolve u prublema di gestisce i profili di l'utilizatori di u prughjettu, deve risponde à e richieste di req-resp è rapportu l'aghjurnamenti di u prufilu via pub-sub. Stu casu hè abbastanza simplice: daretu à a messageria ci hè un controller chì implementa a logica di serviziu è publica l'aghjurnamenti.
A situazione diventa più cumplicata quandu avemu bisognu di implementà un serviziu distribuitu tolerante à i difetti. Imaginemu chì i requisiti per l'utilizatori anu cambiatu:
- avà u serviziu deve processà e dumande nantu à i nodi di cluster 5,
- esse capace di eseguisce travaglii di trasfurmazioni in background,
- è ancu esse capace di gestisce dinamicamente e liste di abbonamenti per l'aghjurnamenti di u prufilu.
Cumentu: Ùn avemu micca cunsideratu u prublema di u almacenamiento coherente è a replicazione di dati. Assumimu chì questi prublemi sò stati risolti prima è u sistema hà digià una strata di almacenamento affidabile è scalabile, è i gestori anu miccanismi per interagisce cun ellu.
A descrizzione formale di u serviziu di l'utilizatori hè diventata più cumplicata. Da u puntu di vista di un programatore, i cambiamenti sò minimi per l'usu di messageria. Per suddisfà u primu requisitu, avemu bisognu di cunfigurà l'equilibriu à u puntu di scambiu req-resp.
U requisitu di processà i travaglii di fondo si trova spessu. In l'utilizatori, questu puderia esse cuntrollà i ducumenti di l'utilizatori, trasfurmà a multimedia scaricata, o sincronizà e dati cù e social media. rete. Questi compiti anu da esse in qualchì modu distribuiti in u cluster è u prugressu di l'esekzione monitoratu. Dunque, avemu duie opzioni di suluzione: o utilizate u mudellu di distribuzione di u travagliu da l'articulu precedente, o, s'ellu ùn hè micca adattatu, scrivite un pianificatore di u travagliu persunalizatu chì gestionerà a piscina di processori in a manera chì avemu bisognu.
U puntu 3 richiede l'estensione di u mudellu pub-sub. È per l'implementazione, dopu avè creatu un puntu di scambiu pub-sub, avemu bisognu di lancià in più u controller di questu puntu in u nostru serviziu. Cusì, hè cum'è si movemu a logica per processà l'abbonamenti è l'annullamenti da a capa di messageria in l'implementazione di l'utilizatori.
In u risultatu, a descomposizione di u prublema hà dimustratu chì per risponde à i bisogni, avemu bisognu di lancià 5 istanze di u serviziu in diversi nodi è creà una entità supplementaria - un controller pub-sub, rispunsevuli di l'abbonamentu.
Per eseguisce 5 handlers, ùn avete micca bisognu di cambià u codice di serviziu. L'unica azzione supplementaria hè di stabilisce e regule di equilibriu à u puntu di scambiu, chì parlemu un pocu dopu.
Ci hè ancu una cumplessità addiziale: u controller pub-sub è u pianificatore di u travagliu persunalizatu deve travaglià in una sola copia. In novu, u serviziu di messageria, cum'è un fundamentale, deve furnisce un mecanismu per selezziunà un capu.
Selezzione di capu
In i sistemi distribuiti, l'elezzione di u capu hè a prucedura per nomina un unicu prucessu rispunsevule per scheduling processing distribuitu di qualchì carica.
In i sistemi chì ùn sò micca propensi à a centralizazione, l'algoritmi universali è basati in cunsensu, cum'è paxos o raft, sò usati.
Siccomu a messageria hè un broker è un elementu cintrali, sapi di tutti i cuntrolli di serviziu - i capi candidati. A messageria pò numinà un capu senza votu.
Dopu avè principiatu è cunnette à u puntu di scambiu, tutti i servizii ricevenu un missaghju di u sistema #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}
. Se LeaderPid
coincide cù pid
prucessu attuale, hè numinatu cum'è u capu, è a lista Servers
include tutti i nodi è i so paràmetri.
À u mumentu un novu appare è un node di cluster di travagliu hè disconnected, tutti i cuntrolli di serviziu ricevenu #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts}
и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts}
rispettivament.
In questu modu, tutti i cumpunenti sò cunuscenti di tutti i cambiamenti, è u cluster hè garantitu per avè un capu in ogni mumentu.
Intermediari
Per implementà prucessi cumplessi di trasfurmazioni distribuite, è ancu in prublemi di ottimisazione di l'architettura esistente, hè cunvenutu à utilizà intermediari.
Per ùn cambià u codice di serviziu è risolve, per esempiu, prublemi di processazione supplementaria, routing o messagi di logging, pudete attivà un gestore di proxy prima di u serviziu, chì farà tuttu u travagliu supplementu.
Un esempiu classicu di ottimisazione pub-sub hè una applicazione distribuita cù un core cummerciale chì genera avvenimenti di aghjurnamentu, cum'è cambiamenti di prezzu in u mercatu, è una capa d'accessu - N servitori chì furnisce una API websocket per i clienti web.
Se decide di fronte, u serviziu di u cliente hè cusì:
- u cliente stabilisce cunnessione cù a piattaforma. Da u latu di u servitore chì finisce u trafficu, un prucessu hè lanciatu per serve sta cunnessione.
- In u cuntestu di u prucessu di serviziu, l'autorizazione è l'abbunamentu à l'aghjurnamenti si verificanu. U prucessu chjama u metudu subscribe per i temi.
- Una volta chì un avvenimentu hè generatu in u kernel, hè furnitu à i prucessi chì servenu e cunnessione.
Imaginemu chì avemu 50000 5 abbonati à u tema "nutizia". L'abbonati sò distribuiti uniformemente in 50000 servitori. In u risultatu, ogni aghjurnamentu, ghjuntu à u puntu di scambiu, serà replicatu 10000 XNUMX volte: XNUMX XNUMX volte in ogni servitore, secondu u numeru di abbonati nantu à questu. Ùn hè micca un schema assai efficace, nò?
Per migliurà a situazione, introducemu un proxy chì hà u listessu nome cum'è u puntu di scambiu. U registratu di u nome glubale deve esse capace di rinvià u prucessu più vicinu per nome, questu hè impurtante.
Lanciamu stu proxy nantu à i servitori di a capa d'accessu, è tutti i nostri prucessi chì servenu l'api websocket abbonaranu à questu, è micca à u puntu di scambiu pub-sub originale in u kernel. Proxy subscribe à u core solu in u casu di un abbonamentu unicu è replicate u missaghju entrante à tutti i so abbonati.
In u risultatu, 5 messagi seranu mandati trà u kernel è i servitori d'accessu, invece di 50000 XNUMX.
Routing è equilibriu
Req-Resp
In l'implementazione di messageria attuale, ci sò 7 strategie di distribuzione di richieste:
default
. A dumanda hè mandata à tutti i cuntrolli.round-robin
. E dumande sò enumerate è distribuite ciclicamente trà i cuntrolli.consensus
. I cuntrolli chì serve u serviziu sò spartuti in capi è schiavi. E dumande sò mandate solu à u capu.consensus & round-robin
. U gruppu hà un capu, ma e dumande sò distribuite trà tutti i membri.sticky
. A funzione hash hè calculata è assignata à un gestore specificu. E dumande dopu cù questa firma vanu à u stessu gestore.sticky-fun
. Quandu inizializza u puntu di scambiu, a funzione di calculu hash persticky
equilibriu.fun
. Simile à sticky-fun, solu pudete ancu reindirizzà, rifiutà o pre-processà.
A strategia di distribuzione hè stabilita quandu u puntu di scambiu hè inizializatu.
In più di l'equilibriu, a messageria permette di tag entità. Fighjemu i tipi di tag in u sistema:
- Tag di cunnessione. Permette di capisce da quale cunnessione sò ghjunti l'avvenimenti. Adupratu quandu un prucessu di cuntrolliu cunnetta à u listessu puntu di scambiu, ma cù diverse chjave di routing.
- Tag di serviziu. Permette di cumminà i gestori in gruppi per un serviziu è espansione e capacità di routing è equilibriu. Per u mudellu req-resp, u routing hè lineare. Mandemu una dumanda à u puntu di scambiu, poi trasmette à u serviziu. Ma s'ellu ci vole à sparte i manigliari in gruppi lògichi, allora a splitting hè fatta cù tags. Quandu si specifica una tag, a dumanda serà mandata à un gruppu specificu di cuntrolli.
- Richiesta tag. Permette di distingue trà e risposte. Siccomu u nostru sistema hè asincronu, per processà e risposte di u serviziu avemu bisognu di pudè specificà un RequestTag quandu invià una dumanda. Da ellu pudemu capisce a risposta à quale dumanda hè ghjunta à noi.
Pub-sub
Per pub-sub tuttu hè un pocu più simplice. Avemu un puntu di scambiu à quale i missaghji sò publicati. U puntu di scambiu distribuisce missaghji trà l'abbonati chì anu abbonatu à i chjavi di routing chì anu bisognu (pudemu dì chì questu hè analogu à i temi).
Scalabilità è tolleranza à i difetti
A scalabilità di u sistema in tuttu dipende di u gradu di scalabilità di i strati è cumpunenti di u sistema:
- I servizii sò scalati aghjustendu nodi supplementari à u cluster cù gestori per stu serviziu. Durante l'operazione di prova, pudete sceglie a pulitica di equilibriu ottimali.
- U serviziu di messageria stessu in un cluster separatu hè generalmente scalatu sia movendu punti di scambiu particularmente caricati in nodi di cluster separati, sia aghjunghjendu prucessi proxy à aree particularmente caricate di u cluster.
- A scalabilità di tuttu u sistema cum'è una caratteristica dipende da a flessibilità di l'architettura è a capacità di cumminà clusters individuali in una entità logica cumuna.
U successu di un prughjettu spessu dipende da a simplicità è a rapidità di scaling. A messageria in a so versione attuale cresce cù l'applicazione. Ancu s'ellu ùn ci manca un cluster di 50-60 machini, pudemu ricorrere à a federazione. Sfurtunatamente, u tema di a federazione hè fora di u scopu di stu articulu.
Riservazione
Quandu analizà u bilanciu di carica, avemu digià discututu a redundanza di i cuntrolli di serviziu. Tuttavia, a messageria deve ancu esse riservata. In l'eventu di un crash di node o macchina, a messageria deve ricuperà automaticamente, è in u più cortu tempu pussibule.
In i mo prughjetti aghju utilizatu nodi supplementari chì piglianu a carica in casu di caduta. Erlang hà una implementazione standard di modu distribuitu per l'applicazioni OTP. U modu distribuitu esegue a ricuperazione in casu di fallimentu lanciandu l'applicazione falluta in un altru node lanciatu prima. U prucessu hè trasparente; dopu un fallimentu, l'applicazione si move automaticamente à u node di failover. Pudete leghje più nantu à sta funziunalità
Produttività
Pruvemu di paragunà almenu apprussimatamente a prestazione di rabbitmq è a nostra messageria persunalizata.
Aghju trovu
In u paràgrafu 6.14.1.2.1.2.2. U documentu originale mostra u risultatu di u RPC CAST:
Ùn faremu micca paràmetri supplementari à u kernel OS o erlang VM in anticipu. Cundizioni per a prova:
- erl opts: +A1 +sbtu.
- A prova in un unicu node erlang hè realizatu nantu à un laptop cù un vechju i7 in versione mobile.
- I testi di cluster sò realizati nantu à i servitori cù una rete 10G.
- U codice corre in cuntenituri docker. Rete in modu NAT.
Codice di prova:
req_resp_bench(_) ->
W = perftest:comprehensive(10000,
fun() ->
messaging:request(?EXCHANGE, default, ping, self()),
receive
#'$msg'{message = pong} -> ok
after 5000 ->
throw(timeout)
end
end
),
true = lists:any(fun(E) -> E >= 30000 end, W),
ok.
Scenariu 1: A prova hè fatta nantu à un laptop cù una vechja versione mobile i7. A prova, a messageria è u serviziu sò eseguiti nantu à un node in un containeru Docker:
Sequential 10000 cycles in ~0 seconds (26987 cycles/s)
Sequential 20000 cycles in ~1 seconds (26915 cycles/s)
Sequential 100000 cycles in ~4 seconds (26957 cycles/s)
Parallel 2 100000 cycles in ~2 seconds (44240 cycles/s)
Parallel 4 100000 cycles in ~2 seconds (53459 cycles/s)
Parallel 10 100000 cycles in ~2 seconds (52283 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (49317 cycles/s)
Scenariu 2: 3 nodi in esecuzione in diverse macchine sottu docker (NAT).
Sequential 10000 cycles in ~1 seconds (8684 cycles/s)
Sequential 20000 cycles in ~2 seconds (8424 cycles/s)
Sequential 100000 cycles in ~12 seconds (8655 cycles/s)
Parallel 2 100000 cycles in ~7 seconds (15160 cycles/s)
Parallel 4 100000 cycles in ~5 seconds (19133 cycles/s)
Parallel 10 100000 cycles in ~4 seconds (24399 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (34517 cycles/s)
In tutti i casi, l'utilizazione di CPU ùn supera u 250%
Risultati
Spergu chì stu ciculu ùn pare micca un dump di mente è a mo sperienza serà di veru benefiziu à i circadori di i sistemi distribuiti è à i pratichi chì sò à u principiu di custruisce architetture distribuite per i so sistemi di cummerciale è fighjenu Erlang / Elixir cun interessu. , ma avete dubbitu vale a pena ...
Photo
Solu l'utilizatori registrati ponu participà à l'indagine.
Chì temi deveru copre in più dettagliu cum'è parte di a serie VTrade Experiment?
-
Teoria: Mercati, ordini è u so timing: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC
-
Libru di ordini. Teoria è pratica di implementà un libru cù raggruppamenti
-
Visualizazione di u cummerciu: Ticks, bars, risoluzioni. Cumu guardà è cumu per cola
-
Backoffice. Pianificazione è sviluppu. Monitoraghju di l'impiegati è investigazione di incidenti
-
API. Scupritemu quali interfacce sò necessarii è cumu implementà
-
Almacenamiento d'infurmazioni: PostgreSQL, Timescale, Tarantool in sistemi di cummerciale
-
Reattività in i sistemi di cummerciale
-
Altru. Scriveraghju in i cumenti
6 utilizatori anu vutatu. 4 utilizatori si sò astenuti.
Source: www.habr.com