Blocchi di costruzione di applicazioni distribuite. Siconda approssimazione

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ù.

Blocchi di costruzione di applicazioni distribuite. Siconda approssimazione

Questu hè l'articulu finale in a serie nantu à l'applicazioni reattive distribuite in Erlang/Elixir. IN primu articulu pudete truvà i fundamenti teorichi di l'architettura reattiva. Secondu articulu illustra i mudelli basi è i meccanismi per a custruzzione di tali sistemi.

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:

  1. avà u serviziu deve processà e dumande nantu à i nodi di cluster 5,
  2. esse capace di eseguisce travaglii di trasfurmazioni in background,
  3. è 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 per sticky 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à ccà.

Produttività

Pruvemu di paragunà almenu apprussimatamente a prestazione di rabbitmq è a nostra messageria persunalizata.
Aghju trovu risultati ufficiali test rabbitmq da a squadra openstack.

In u paràgrafu 6.14.1.2.1.2.2. U documentu originale mostra u risultatu di u RPC CAST:
Blocchi di costruzione di applicazioni distribuite. Siconda approssimazione

Ù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 @chuttersnap

Solu l'utilizatori registrati ponu participà à l'indagine. Firmà lu, per piacè.

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

Add a comment