Građevinski blokovi distribuiranih aplikacija. Druga aproksimacija

Najava

Kolege, sredinom ljeta planiram da objavim još jednu seriju članaka o dizajnu sistema čekanja: “VTrade Experiment” - pokušaj da se napiše okvir za sisteme trgovanja. Serija će ispitati teoriju i praksu izgradnje berze, aukcije i prodavnice. Na kraju članka pozivam vas da glasate za teme koje vas najviše zanimaju.

Građevinski blokovi distribuiranih aplikacija. Druga aproksimacija

Ovo je posljednji članak u seriji o distribuiranim reaktivnim aplikacijama u Erlang/Elixir-u. IN prvi članak možete pronaći teorijske osnove reaktivne arhitekture. Drugi članak ilustruje osnovne obrasce i mehanizme za konstruisanje ovakvih sistema.

Danas ćemo pokrenuti pitanja razvoja baze kodova i projekata općenito.

Organizacija usluga

U stvarnom životu, kada razvijate uslugu, često morate kombinirati nekoliko obrazaca interakcije u jednom kontroleru. Na primjer, korisnički servis, koji rješava problem upravljanja projektnim korisničkim profilima, mora odgovoriti na req-resp zahtjeve i prijaviti ažuriranja profila putem pub-sub-a. Ovaj slučaj je prilično jednostavan: iza razmjene poruka postoji jedan kontroler koji implementira logiku usluge i objavljuje ažuriranja.

Situacija postaje složenija kada trebamo implementirati distribuirani servis otporan na greške. Zamislimo da su se zahtjevi za korisnike promijenili:

  1. sada bi servis trebao obraditi zahtjeve na 5 čvorova klastera,
  2. biti u stanju obavljati zadatke obrade u pozadini,
  3. i također moći dinamički upravljati listama pretplate za ažuriranja profila.

Komentar: Ne razmatramo pitanje dosljedne pohrane i replikacije podataka. Pretpostavimo da su ovi problemi ranije riješeni i da sistem već ima pouzdan i skalabilan sloj za skladištenje, a rukovaoci imaju mehanizme za interakciju s njim.

Formalni opis korisničke usluge postao je složeniji. Sa stanovišta programera, promjene su minimalne zbog korištenja poruka. Da bismo zadovoljili prvi zahtjev, moramo konfigurirati balansiranje na tački razmjene req-resp.

Zahtjev za obradom pozadinskih zadataka se često javlja. Kod korisnika to može biti provjera korisničkih dokumenata, obrada preuzete multimedije ili sinkronizacija podataka s društvenim medijima. mreže. Ovi zadaci se moraju nekako distribuirati unutar klastera i pratiti napredak izvršenja. Stoga imamo dvije opcije rješenja: ili koristiti predložak distribucije zadataka iz prethodnog članka ili, ako ne odgovara, napisati prilagođeni planer zadataka koji će upravljati skupom procesora na način koji nam je potreban.

Točka 3 zahtijeva ekstenziju šablona pub-sub. A za implementaciju, nakon kreiranja pub-sub tačke razmjene, potrebno je dodatno pokrenuti kontroler ove tačke u okviru našeg servisa. Dakle, kao da premeštamo logiku obrade pretplata i odjava sa sloja za razmenu poruka u implementaciju korisnika.

Kao rezultat toga, dekompozicija problema je pokazala da da bismo ispunili zahtjeve, moramo pokrenuti 5 instanci servisa na različitim čvorovima i kreirati dodatni entitet - pub-sub kontroler, odgovoran za pretplatu.
Da biste pokrenuli 5 rukovatelja, ne morate mijenjati servisni kod. Jedina dodatna radnja je postavljanje pravila balansiranja na mjestu razmjene, o čemu ćemo govoriti nešto kasnije.
Postoji i dodatna složenost: pub-sub kontroler i prilagođeni planer zadataka moraju raditi u jednoj kopiji. Opet, servis za razmjenu poruka, kao osnovni, mora obezbijediti mehanizam za odabir lidera.

Izbor lidera

U distribuiranim sistemima, izbor lidera je procedura za imenovanje jednog procesa odgovornog za zakazivanje distribuirane obrade nekog opterećenja.

U sistemima koji nisu skloni centralizaciji, koriste se univerzalni algoritmi zasnovani na konsenzusu, kao što su paxos ili raft.
Budući da je razmjena poruka posrednik i centralni element, ona zna za sve kontrolore usluga - kandidate lidere. Messaging može imenovati vođu bez glasanja.

Nakon pokretanja i povezivanja na točku razmjene, sve usluge dobijaju sistemsku poruku #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}... Ako LeaderPid poklapa se sa pid trenutnog procesa, imenovan je za lidera i listu Servers uključuje sve čvorove i njihove parametre.
U trenutku kada se pojavi novi i radni čvor klastera je isključen, svi servisni kontroleri primaju #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} respektivno.

Na ovaj način, sve komponente su svjesne svih promjena, a klaster je zagarantovan da ima jednog lidera u svakom trenutku.

Posrednici

Za implementaciju složenih procesa distribuirane obrade, kao iu problemima optimizacije postojeće arhitekture, zgodno je koristiti posrednike.
Kako ne biste mijenjali servisni kod i rješavali, na primjer, probleme dodatne obrade, rutiranja ili evidentiranja poruka, prije servisa možete omogućiti proxy handler koji će obavljati sav dodatni posao.

Klasičan primjer pub-sub optimizacije je distribuirana aplikacija s poslovnom jezgrom koja generiše događaje ažuriranja, kao što su promjene cijena na tržištu, i pristupni sloj - N serveri koji pružaju websocket API za web klijente.
Ako se odlučite direktno, onda služba za korisnike izgleda ovako:

  • klijent uspostavlja veze sa platformom. Na strani servera koji prekida promet, pokreće se proces za servisiranje ove veze.
  • U kontekstu procesa usluge, dolazi do autorizacije i pretplate na ažuriranja. Proces poziva metodu pretplate za teme.
  • Kada se događaj generiše u kernelu, on se isporučuje procesima koji opslužuju veze.

Zamislimo da imamo 50000 pretplatnika na temu "vijesti". Pretplatnici su ravnomjerno raspoređeni na 5 servera. Kao rezultat, svako ažuriranje, koje stigne na tačku razmene, biće replicirano 50000 puta: 10000 puta na svakom serveru, u skladu sa brojem pretplatnika na njemu. Nije baš efikasna šema, zar ne?
Da popravimo situaciju, uvedemo proxy koji ima isto ime kao i tačka razmjene. Globalni registrator imena mora biti u mogućnosti da vrati najbliži proces po imenu, ovo je važno.

Pokrenimo ovaj proxy na serverima pristupnog sloja i svi naši procesi koji opslužuju websocket api će se pretplatiti na njega, a ne na originalnu pub-sub točku razmjene u kernelu. Proxy se pretplaćuje na jezgro samo u slučaju jedinstvene pretplate i replicira dolaznu poruku svim svojim pretplatnicima.
Kao rezultat, 5 poruka će biti poslano između kernela i pristupnih servera, umjesto 50000.

Usmjeravanje i balansiranje

Req-Resp

U trenutnoj implementaciji poruka postoji 7 strategija distribucije zahtjeva:

  • default. Zahtjev se šalje svim kontrolorima.
  • round-robin. Zahtjevi se nabrajaju i ciklički distribuiraju između kontrolora.
  • consensus. Kontrolori koji služe službi dijele se na vođe i robove. Zahtjevi se šalju samo voditelju.
  • consensus & round-robin. Grupa ima vođu, ali se zahtjevi distribuiraju svim članovima.
  • sticky. Heš funkcija se izračunava i dodjeljuje određenom rukovaocu. Naredni zahtjevi s ovim potpisom idu istom rukovaocu.
  • sticky-fun. Prilikom inicijalizacije točke razmjene, funkcija izračuna hash za sticky balansiranje.
  • fun. Slično kao sticky-fun, samo vi to možete dodatno preusmjeriti, odbaciti ili prethodno obraditi.

Strategija distribucije se postavlja kada se inicijalizira tačka razmjene.

Pored balansiranja, razmjena poruka vam omogućava da označite entitete. Pogledajmo vrste oznaka u sistemu:

  • Oznaka veze. Omogućava vam da shvatite kroz koju vezu su događaji došli. Koristi se kada se proces kontrolera povezuje na istu točku razmjene, ali s različitim ključevima za rutiranje.
  • Servisna oznaka. Omogućava vam da kombinujete rukovaoce u grupe za jednu uslugu i proširite mogućnosti usmjeravanja i balansiranja. Za obrazac req-resp, usmjeravanje je linearno. Šaljemo zahtjev na mjesto zamjene, a zatim ga prosljeđuje servisu. Ali ako trebamo podijeliti rukovaoce u logičke grupe, onda se podjela vrši pomoću oznaka. Prilikom navođenja oznake, zahtjev će biti poslan određenoj grupi kontrolora.
  • Zatražite oznaku. Omogućava vam da razlikujete odgovore. Pošto je naš sistem asinhroni, za obradu odgovora usluge moramo biti u mogućnosti navesti RequestTag prilikom slanja zahtjeva. Iz njega ćemo moći razumjeti odgovor na koji nam je zahtjev stigao.

Pub-sub

Za pub-sub sve je malo jednostavnije. Imamo razmjenu na kojoj se objavljuju poruke. Tačka razmene distribuira poruke među pretplatnicima koji su se pretplatili na ključeve za rutiranje koji su im potrebni (možemo reći da je to analogno temama).

Skalabilnost i tolerancija grešaka

Skalabilnost sistema u celini zavisi od stepena skalabilnosti slojeva i komponenti sistema:

  • Usluge se skaliraju dodavanjem dodatnih čvorova u klaster s rukovaocima za ovu uslugu. Tokom probnog rada možete odabrati optimalnu politiku balansiranja.
  • Sama usluga za razmjenu poruka unutar zasebnog klastera općenito se skalira ili premeštanjem posebno učitanih točaka razmjene u odvojene čvorove klastera, ili dodavanjem proxy procesa u posebno učitana područja klastera.
  • Skalabilnost čitavog sistema kao karakteristika zavisi od fleksibilnosti arhitekture i mogućnosti kombinovanja pojedinačnih klastera u zajednički logički entitet.

Uspjeh projekta često ovisi o jednostavnosti i brzini skaliranja. Razmjena poruka u svojoj trenutnoj verziji raste zajedno s aplikacijom. Čak i ako nam nedostaje klaster od 50-60 mašina, možemo pribjeći federaciji. Nažalost, tema federacije je izvan okvira ovog članka.

Rezervacija

Kada smo analizirali balansiranje opterećenja, već smo razgovarali o redundantnosti servisnih kontrolera. Međutim, razmjena poruka također mora biti rezervirana. U slučaju pada čvora ili mašine, razmjena poruka bi se trebala automatski oporaviti i to u najkraćem mogućem roku.

U svojim projektima koristim dodatne čvorove koji preuzimaju opterećenje u slučaju pada. Erlang ima standardnu ​​implementaciju distribuiranog načina za OTP aplikacije. Distribuirani način obavlja oporavak u slučaju kvara pokretanjem neuspjele aplikacije na drugom prethodno pokrenutom čvoru. Proces je transparentan; nakon neuspjeha, aplikacija se automatski pomiče na čvor za nadilaženje greške. Možete pročitati više o ovoj funkciji ovdje.

Produktivnost

Pokušajmo barem otprilike uporediti performanse rabbitmq-a i našeg prilagođenog slanja poruka.
našao sam zvanični rezultati rabbitmq testiranje od openstack tima.

U stavu 6.14.1.2.1.2.2. Originalni dokument prikazuje rezultat RPC CAST:
Građevinski blokovi distribuiranih aplikacija. Druga aproksimacija

Nećemo unapred vršiti nikakva dodatna podešavanja za kernel OS ili erlang VM. Uslovi za testiranje:

  • erl opts: +A1 +sbtu.
  • Test unutar jednog erlang čvora se izvodi na laptopu sa starim i7 u mobilnoj verziji.
  • Klaster testovi se provode na serverima sa 10G mrežom.
  • Kod se izvodi u docker kontejnerima. Mreža u NAT modu.

Test kod:

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.

Scenario 1: Test se izvodi na laptopu sa starom i7 mobilnom verzijom. Test, slanje poruka i usluga se izvode na jednom čvoru u jednom Docker kontejneru:

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)

Scenario 2: 3 čvora koji rade na različitim mašinama pod dockerom (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)

U svim slučajevima, iskorištenost CPU-a nije prelazila 250%

Ishodi

Nadam se da ovaj ciklus neće izgledati kao smetlište uma i moje iskustvo će biti od velike koristi kako istraživačima distribuiranih sistema tako i praktičarima koji su na samom početku izgradnje distribuiranih arhitektura za svoje poslovne sisteme i sa zanimanjem gledaju na Erlang/Elixir , ali sumnjam da li vredi...

fotografija @chuttersnap

Samo registrovani korisnici mogu učestvovati u anketi. Prijavite semolim.

Koje teme treba da obradim detaljnije u sklopu serije VTrade Experiment?

  • Teorija: Tržišta, nalozi i njihovo vrijeme: DAY, GTD, GTC, MOC, FOK, MOO, MOC, LOO, LOC

  • Knjiga narudžbi. Teorija i praksa implementacije knjige sa grupama

  • Vizualizacija trgovanja: Tikovi, barovi, rezolucije. Kako čuvati i lepiti

  • Backoffice. Planiranje i razvoj. Praćenje zaposlenih i istraga incidenta

  • API. Hajde da shvatimo koja su sučelja potrebna i kako ih implementirati

  • Skladištenje informacija: PostgreSQL, Timescale, Tarantool u sistemima trgovanja

  • Reaktivnost u sistemima trgovanja

  • Ostalo. Pisaću u komentarima

Glasalo je 6 korisnika. 4 korisnika je bila uzdržana.

izvor: www.habr.com

Dodajte komentar