Hajautettujen sovellusten rakennuspalikoita. Toinen likiarvo

ilmoitus

Kollegat, keskikesällä aion julkaista toisen artikkelisarjan jonojärjestelmien suunnittelusta: "VTrade Experiment" - yritys kirjoittaa puitteet kaupankäyntijärjestelmille. Sarjassa tarkastellaan pörssin, huutokaupan ja myymälän rakentamisen teoriaa ja käytäntöä. Artikkelin lopussa kehotan sinua äänestämään sinua eniten kiinnostavia aiheita.

Hajautettujen sovellusten rakennuspalikoita. Toinen likiarvo

Tämä on Erlang/Elixirin hajautettuja reaktiivisia sovelluksia käsittelevän sarjan viimeinen artikkeli. SISÄÄN ensimmäinen artikkeli löydät reaktiivisen arkkitehtuurin teoreettiset perusteet. Toinen artikkeli kuvaa tällaisten järjestelmien rakentamisen perusmalleja ja -mekanismeja.

Tänään otamme esille koodikannan kehittämistä ja projekteja yleensä koskevia kysymyksiä.

Palvelujen järjestäminen

Tosielämässä palvelua kehitettäessä joutuu usein yhdistämään useita vuorovaikutusmalleja yhteen ohjaimeen. Esimerkiksi käyttäjäpalvelun, joka ratkaisee projektin käyttäjäprofiilien hallinnan ongelman, tulee vastata req-resp-pyyntöihin ja raportoida profiilipäivitykset pub-sub:n kautta. Tämä tapaus on melko yksinkertainen: viestinnän takana on yksi ohjain, joka toteuttaa palvelulogiikan ja julkaisee päivitykset.

Tilanne monimutkaistuu, kun joudumme toteuttamaan vikasietoisen hajautetun palvelun. Kuvitellaan, että käyttäjiä koskevat vaatimukset ovat muuttuneet:

  1. nyt palvelun pitäisi käsitellä pyynnöt 5 klusterin solmussa,
  2. osaa suorittaa taustakäsittelytehtäviä,
  3. ja pystyä myös hallitsemaan dynaamisesti profiilipäivitysten tilausluetteloita.

huomautus: Emme ota huomioon johdonmukaista tallennusta ja tietojen replikointia. Oletetaan, että nämä ongelmat on ratkaistu aikaisemmin ja järjestelmässä on jo luotettava ja skaalautuva tallennuskerros ja käsittelijöillä on mekanismeja vuorovaikutukseen sen kanssa.

Käyttäjäpalvelun muodollinen kuvaus on monimutkaistunut. Ohjelmoijan näkökulmasta muutokset ovat minimaalisia johtuen viestinnän käytöstä. Ensimmäisen vaatimuksen täyttämiseksi meidän on määritettävä tasapainotus req-resp-vaihtopisteessä.

Tarve käsitellä taustatehtäviä esiintyy usein. Käyttäjillä tämä voi olla käyttäjän asiakirjojen tarkistamista, ladatun multimedian käsittelyä tai tietojen synkronointia sosiaalisen median kanssa. verkkoja. Nämä tehtävät on jaettava jotenkin klusterin sisällä ja suorituksen edistymistä seurataan. Siksi meillä on kaksi ratkaisuvaihtoehtoa: joko käytä edellisen artikkelin tehtävänjakomallia tai, jos se ei sovi, kirjoita mukautettu tehtävän ajoitus, joka hallitsee prosessorivalikoimaa haluamallamme tavalla.

Kohta 3 vaatii pub-sub-mallin laajennuksen. Ja käyttöönottoa varten, kun olemme luoneet pub-sub-vaihtopisteen, meidän on lisäksi käynnistettävä tämän pisteen ohjain palvelussamme. Näin ollen on ikään kuin siirrämme tilausten ja tilausten käsittelyn logiikkaa viestikerroksesta käyttäjien toteutukseen.

Tämän seurauksena ongelman purkaminen osoitti, että vaatimusten täyttämiseksi meidän on käynnistettävä 5 palvelun esiintymää eri solmuissa ja luotava lisäkokonaisuus - pub-sub-ohjain, joka vastaa tilauksesta.
Jotta voit suorittaa 5 käsittelijää, sinun ei tarvitse muuttaa palvelukoodia. Ainoa lisätoimenpide on tasapainotussääntöjen asettaminen vaihtopisteeseen, josta puhumme hieman myöhemmin.
On myös lisämonimutkaisuus: pub-sub-ohjaimen ja mukautetun tehtävien ajoituksen on toimittava yhdessä kopiossa. Jälleen viestintäpalvelun perustavanlaatuisena palveluna on tarjottava mekanismi johtajan valitsemiseksi.

Johtajan valinta

Hajautetuissa järjestelmissä johtajan valinta on menettely, jolla nimetään yksi prosessi, joka vastaa jonkin kuorman hajautetun käsittelyn ajoittamisesta.

Järjestelmissä, jotka eivät ole alttiita keskittämiselle, käytetään universaaleja ja konsensuspohjaisia ​​algoritmeja, kuten paxoja tai lautta.
Koska viestintä on välittäjä ja keskeinen elementti, se tietää kaikista palvelunohjaajista - johtajaehdokkaista. Viestit voivat nimittää johtajan ilman äänestystä.

Käynnistyksen ja vaihtopisteeseen yhdistämisen jälkeen kaikki palvelut saavat järjestelmäviestin #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Jos LeaderPid samaan aikaan pid Nykyinen prosessi, se nimitetään johtajaksi ja luetteloon Servers sisältää kaikki solmut ja niiden parametrit.
Tällä hetkellä uusi klusterisolmu ilmestyy ja toimiva klusterisolmu on katkaistu, kaikki palveluohjaimet saavat #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} vastaavasti.

Näin kaikki komponentit ovat tietoisia kaikista muutoksista, ja klusterilla on taatusti yksi johtaja kerrallaan.

välittäjien

Monimutkaisten hajautettujen käsittelyprosessien toteuttamiseen sekä olemassa olevan arkkitehtuurin optimointiongelmiin on kätevää käyttää välittäjiä.
Jotta palvelukoodia ei muuteta ja esimerkiksi viestien lisäkäsittely-, reititys- tai lokiongelmat ratkeavat, voit ottaa käyttöön välityspalvelimen käsittelijän ennen palvelua, joka suorittaa kaikki lisätyöt.

Klassinen esimerkki pub-sub-optimoinnista on hajautettu sovellus, jossa on liiketoimintaydin, joka luo päivitystapahtumia, kuten hintamuutoksia markkinoilla, ja pääsykerros - N palvelinta, jotka tarjoavat websocket-sovellusliittymän verkkoasiakkaille.
Jos päätät suoraan, asiakaspalvelu näyttää tältä:

  • asiakas muodostaa yhteydet alustaan. Palvelimen puolella, joka katkaisee liikenteen, käynnistetään prosessi tämän yhteyden palvelemiseksi.
  • Palveluprosessin yhteydessä tapahtuu valtuutus ja päivitysten tilaaminen. Prosessi kutsuu aiheiden tilausmenetelmää.
  • Kun tapahtuma on luotu ytimessä, se toimitetaan yhteyksiä palveleville prosesseille.

Kuvitellaan, että meillä on 50000 5 "uutisten" tilaajaa. Tilaajat jakautuvat tasaisesti 50000 palvelimelle. Tämän seurauksena jokainen vaihtopisteeseen saapuva päivitys kopioidaan 10000 XNUMX kertaa: XNUMX XNUMX kertaa kullakin palvelimella sen tilaajamäärän mukaan. Ei kovin tehokas järjestelmä, eihän?
Tilanteen parantamiseksi otetaan käyttöön välityspalvelin, jolla on sama nimi kuin vaihtopisteellä. Globaalin nimenrekisteröijän on kyettävä palauttamaan lähin prosessi nimellä, tämä on tärkeää.

Käynnistetään tämä välityspalvelin pääsykerroksen palvelimilla, ja kaikki websocket-sovellusliittymää palvelevat prosessimme tilaavat sen, eivät ytimen alkuperäistä pub-sub-vaihtopistettä. Välityspalvelin tilaa ytimen vain yksilöllisen tilauksen tapauksessa ja replikoi saapuvan viestin kaikille tilaajilleen.
Tämän seurauksena ytimen ja pääsypalvelinten välillä lähetetään 5 viestiä 50000 XNUMX sijaan.

Reititys ja tasapainotus

Req-Resp

Nykyisessä viestintätoteutuksessa on 7 pyyntöjen jakelustrategiaa:

  • default. Pyyntö lähetetään kaikille valvojille.
  • round-robin. Pyynnöt luetellaan ja jaetaan syklisesti ohjaimien kesken.
  • consensus. Palvelua palvelevat valvojat on jaettu johtajiin ja orjiin. Pyynnöt lähetetään vain johtajalle.
  • consensus & round-robin. Ryhmällä on johtaja, mutta pyynnöt jaetaan kaikkien jäsenten kesken.
  • sticky. Hajautusfunktio lasketaan ja määrätään tietylle käsittelijälle. Myöhemmät tällä allekirjoituksella tehdyt pyynnöt menevät samalle käsittelijälle.
  • sticky-fun. Kun alustetaan vaihtopistettä, tiivistelaskentatoiminto for sticky tasapainottaa.
  • fun. Kuten sticky-fun, vain sinä voit lisäksi uudelleenohjata, hylätä tai esikäsitellä sen.

Jakelustrategia asetetaan, kun vaihtopiste alustetaan.

Tasapainotuksen lisäksi viestien avulla voit merkitä entiteettejä. Katsotaanpa järjestelmän tunnistetyyppejä:

  • Yhteysmerkki. Auttaa ymmärtämään, minkä yhteyden kautta tapahtumat tulivat. Käytetään, kun ohjainprosessi muodostaa yhteyden samaan vaihtopisteeseen, mutta eri reititysavaimilla.
  • Huoltomerkki. Voit yhdistää käsittelijät ryhmiin yhtä palvelua varten ja laajentaa reititys- ja tasapainotusominaisuuksia. Req-resp -kuviolle reititys on lineaarinen. Lähetämme pyynnön vaihtopisteeseen, jonka jälkeen se välittää sen palveluun. Mutta jos meidän on jaettava käsittelijät loogisiin ryhmiin, jakaminen tapahtuu tagien avulla. Tunnistetta määritettäessä pyyntö lähetetään tietylle ohjaimien ryhmälle.
  • Pyydä tunniste. Voit erottaa vastaukset toisistaan. Koska järjestelmämme on asynkroninen, palveluvastausten käsittelyä varten meidän on voitava määrittää RequestTag pyyntöä lähetettäessä. Siitä voimme ymmärtää vastauksen, johon pyyntömme tuli.

Pub-sub

Pub-subissa kaikki on hieman yksinkertaisempaa. Meillä on vaihtopiste, johon viestit julkaistaan. Vaihtopiste jakaa viestejä tilaajille, jotka ovat tilaaneet tarvitsemansa reititysavaimet (voidaan sanoa, että tämä vastaa aiheita).

Skaalautuvuus ja vikasietoisuus

Koko järjestelmän skaalautuvuus riippuu järjestelmän kerrosten ja komponenttien skaalautuvuusasteesta:

  • Palvelut skaalataan lisäämällä klusteriin lisäsolmuja tämän palvelun käsittelijöiden kanssa. Koekäytön aikana voit valita optimaalisen tasapainotuspolitiikan.
  • Itse viestintäpalvelu erillisessä klusterissa skaalataan yleensä joko siirtämällä erityisen kuormitettuja vaihtopisteitä erillisiin klusterisolmuihin tai lisäämällä välityspalvelinprosesseja klusterin erityisen kuormitetuille alueille.
  • Koko järjestelmän skaalautuvuus ominaisuutena riippuu arkkitehtuurin joustavuudesta ja kyvystä yhdistää yksittäisiä klustereita yhteiseksi loogiseksi kokonaisuudeksi.

Projektin onnistuminen riippuu usein skaalauksen yksinkertaisuudesta ja nopeudesta. Viestit nykyisessä versiossaan kasvavat sovelluksen mukana. Vaikka 50-60 koneen klusteri puuttuisikin, voimme turvautua liittoutumiseen. Valitettavasti liiton aihe ei kuulu tämän artikkelin piiriin.

Varaus

Kuormituksen tasapainotusta analysoitaessa keskustelimme jo palveluohjaimien redundanssista. Viestit on kuitenkin myös varattava. Solmun tai koneen kaatuessa viestien pitäisi palautua automaattisesti ja mahdollisimman lyhyessä ajassa.

Projekteissani käytän lisäsolmuja, jotka ottavat kuorman kaatumisen sattuessa. Erlangilla on standardi hajautetun tilan toteutus OTP-sovelluksille. Hajautettu tila suorittaa palautuksen epäonnistumisen sattuessa käynnistämällä epäonnistuneen sovelluksen toisessa aiemmin käynnistetyssä solmussa. Prosessi on läpinäkyvä; vian jälkeen sovellus siirtyy automaattisesti vikasietosolmuun. Voit lukea lisää tästä toiminnosta täällä.

Suorituskyky

Yritetään ainakin karkeasti verrata rabbitmq:n suorituskykyä ja mukautettua viestiämme.
löysin viralliset tulokset rabbitmq-testaus openstack-tiimiltä.

Kohdassa 6.14.1.2.1.2.2. Alkuperäinen asiakirja näyttää RPC CASTin tuloksen:
Hajautettujen sovellusten rakennuspalikoita. Toinen likiarvo

Emme tee lisäasetuksia käyttöjärjestelmän ytimeen tai erlang VM:ään etukäteen. Testauksen ehdot:

  • erl opts: +A1 +sbtu.
  • Testi yhden erlang-solmun sisällä suoritetaan kannettavalla tietokoneella, jossa on vanha i7 mobiiliversiossa.
  • Klusteritestit suoritetaan palvelimilla, joissa on 10G-verkko.
  • Koodi toimii docker-säiliöissä. Verkko NAT-tilassa.

Testikoodi:

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.

Skenaario 1: Testi suoritetaan kannettavalla tietokoneella, jossa on vanha i7-mobiiliversio. Testi, viestit ja palvelu suoritetaan yhdessä solmussa yhdessä Docker-säiliössä:

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)

Skenaario 2: 3 solmua, jotka toimivat eri koneissa dockerin (NAT) alla.

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)

Kaikissa tapauksissa suorittimen käyttöaste ei ylittänyt 250 %

Tulokset

Toivon, että tämä sykli ei näytä mielen kaatopaikalta ja kokemuksestani on todellista hyötyä sekä hajautettujen järjestelmien tutkijoille että toimijoille, jotka ovat aivan alussa rakentamassa hajautettuja arkkitehtuureja liiketoimintajärjestelmilleen ja katsovat kiinnostuneena Erlangia/Elixiriä. , mutta epäilen onko sen arvoista...

Photo Shoot @chuttersnap

Vain rekisteröityneet käyttäjät voivat osallistua kyselyyn. Kirjaudu sisään, ole kiltti.

Mitä aiheita minun pitäisi käsitellä tarkemmin osana VTrade Experiment -sarjaa?

  • Teoria: Markkinat, tilaukset ja niiden ajoitus: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Tilauskirja. Teoria ja käytäntö kirjan toteuttamisesta ryhmittelyillä

  • Kaupankäynnin visualisointi: Tikit, palkit, päätöslauselmat. Kuinka säilyttää ja kuinka liimata

  • Takahuone. Suunnittelu ja kehitys. Työntekijöiden seuranta ja tapahtumien tutkinta

  • API. Selvitetään, mitä rajapintoja tarvitaan ja miten ne toteutetaan

  • Tiedon tallennus: PostgreSQL, Timescale, Tarantool kaupankäyntijärjestelmissä

  • Reaktiivisuus kaupankäyntijärjestelmissä

  • muu. Kirjoitan kommentteihin

6 käyttäjää äänesti. 4 käyttäjää pidättyi äänestämästä.

Lähde: will.com

Lisää kommentti