Az elosztott alkalmazások építőkövei. Második közelítés

Közlemény

Kollégák, a nyár közepén egy újabb cikksorozatot tervezek kiadni a sorbanállási rendszerek tervezéséről: „The VTrade Experiment” - kísérlet egy keretrendszer megírására a kereskedési rendszerek számára. A sorozat a tőzsde, az aukció és az üzlet felépítésének elméletét és gyakorlatát vizsgálja. A cikk végén arra kérlek benneteket, hogy szavazzatok az Önt leginkább érdeklő témákra.

Az elosztott alkalmazások építőkövei. Második közelítés

Ez az Erlang/Elixir elosztott reaktív alkalmazásairól szóló sorozat utolsó cikke. BAN BEN első cikk megtalálhatja a reaktív építészet elméleti alapjait. Második cikk szemlélteti az ilyen rendszerek felépítésének alapvető mintáit és mechanizmusait.

Ma a kódbázis fejlesztésével és általában a projektekkel kapcsolatos kérdéseket fogjuk felvetni.

Szolgáltatások szervezése

A való életben egy szolgáltatás fejlesztése során gyakran több interakciós mintát kell kombinálnia egy vezérlőben. Például a felhasználói szolgáltatásnak, amely megoldja a projekt felhasználói profiljainak kezelését, válaszolnia kell a req-resp kérésekre, és jelentenie kell a profilfrissítéseket a pub-sub-on keresztül. Ez az eset nagyon egyszerű: az üzenetküldés mögött van egy vezérlő, amely megvalósítja a szolgáltatási logikát és közzéteszi a frissítéseket.

A helyzet bonyolultabbá válik, ha egy hibatűrő elosztott szolgáltatást kell megvalósítanunk. Képzeljük el, hogy a felhasználókkal szemben támasztott követelmények megváltoztak:

  1. most a szolgáltatásnak 5 fürtcsomóponton kell feldolgoznia a kéréseket,
  2. képes legyen háttérfeldolgozási feladatok elvégzésére,
  3. és képes legyen dinamikusan kezelni a profilfrissítésekre vonatkozó előfizetési listákat.

megjegyzés: Nem foglalkozunk a következetes tárolás és adatreplikáció kérdésével. Tegyük fel, hogy ezeket a problémákat korábban megoldották, és a rendszer már rendelkezik egy megbízható és méretezhető tárolóréteggel, és a kezelők rendelkeznek a vele való interakcióra szolgáló mechanizmusokkal.

A felhasználói szolgáltatás formális leírása bonyolultabbá vált. Programozói szempontból minimálisak a változtatások az üzenetküldés használata miatt. Az első követelmény teljesítéséhez be kell állítani a kiegyenlítést a req-resp csereponton.

A háttérfeladatok feldolgozásának követelménye gyakran előfordul. A felhasználóknál ez lehet a felhasználói dokumentumok ellenőrzése, a letöltött multimédia feldolgozása vagy az adatok szinkronizálása a közösségi médiával. hálózatok. Ezeket a feladatokat valahogy el kell osztani a fürtön belül, és figyelemmel kell kísérni a végrehajtás előrehaladását. Ezért két megoldási lehetőségünk van: vagy az előző cikkben szereplő feladatelosztási sablont használjuk, vagy ha nem felel meg, írjunk egy egyéni feladatütemezőt, amely a szükséges módon kezeli a processzorkészletet.

A 3. pont megköveteli a pub-sub sablon kiterjesztését. A megvalósításhoz pedig egy pub-sub cserepont létrehozása után a szolgáltatásunkon belül ennek a pontnak a vezérlőjét is el kell indítanunk. Így olyan, mintha az előfizetések és leiratkozások feldolgozásának logikáját az üzenetküldő rétegből a felhasználók megvalósításába helyeznénk át.

Ennek eredményeként a probléma bontása azt mutatta, hogy a követelmények teljesítéséhez a szolgáltatás 5 példányát kell elindítanunk különböző csomópontokon, és létre kell hoznunk egy további entitást - egy pub-sub vezérlőt, amely az előfizetésért felelős.
5 kezelő futtatásához nem kell módosítania a szervizkódot. Az egyetlen további lépés a kiegyenlítési szabályok felállítása a csereponton, amelyről egy kicsit később fogunk beszélni.
Van egy további bonyolultság is: a közzétételi alvezérlőnek és az egyéni feladatütemezőnek egyetlen példányban kell működnie. Ismét az üzenetküldő szolgáltatásnak, mint alapvetőnek, egy mechanizmust kell biztosítania a vezető kiválasztásához.

Vezető kiválasztása

Az elosztott rendszerekben a vezetőválasztás egyetlen folyamat kijelölésére szolgál, amely bizonyos terhelések elosztott feldolgozásának ütemezéséért felelős.

Azokban a rendszerekben, amelyek nem hajlamosak a központosításra, univerzális és konszenzus alapú algoritmusokat, például paxókat vagy raftokat használnak.
Mivel az üzenetküldés közvetítő és központi elem, minden szolgáltatásvezérlőről – vezetőjelöltről – tud. Az üzenetküldés szavazás nélkül is kijelölhet vezetőt.

A cserepont elindítása és csatlakozása után minden szolgáltatás rendszerüzenetet kap #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Ha LeaderPid egybeesik pid folyamatban van, azt nevezik ki vezetőnek, és a lista Servers tartalmazza az összes csomópontot és azok paramétereit.
Abban a pillanatban, amikor megjelenik egy új, és egy működő fürtcsomópont le van választva, az összes szolgáltatásvezérlő megkapja #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} volt.

Így minden komponens tisztában van az összes változással, és a klaszternek garantáltan mindig van egy vezetője.

Közvetítők

Az összetett elosztott feldolgozási folyamatok megvalósításához, valamint a meglévő architektúra optimalizálásának problémáihoz kényelmes közvetítőket használni.
Annak érdekében, hogy ne módosítsa a szolgáltatáskódot, és ne oldja meg például az üzenetek további feldolgozásával, útválasztásával vagy naplózásával kapcsolatos problémákat, engedélyezhet egy proxykezelőt a szolgáltatás előtt, amely elvégzi az összes további munkát.

A pub-sub optimalizálás klasszikus példája egy elosztott alkalmazás, amelynek üzleti magja frissítési eseményeket generál, például árváltozásokat a piacon, valamint hozzáférési réteg – N szerver, amely websocket API-t biztosít a webkliensek számára.
Ha előre dönt, az ügyfélszolgálat így néz ki:

  • az ügyfél kapcsolatot létesít a platformmal. A kiszolgáló azon oldalán, amelyik megszünteti a forgalmat, elindul egy folyamat a kapcsolat kiszolgálására.
  • A szolgáltatási folyamat keretében történik a frissítésekre vonatkozó engedélyezés és előfizetés. A folyamat meghívja a témák előfizetési módszerét.
  • Ha egy esemény létrejött a kernelben, az elküldésre kerül a kapcsolatokat kiszolgáló folyamatokhoz.

Képzeljük el, hogy 50000 5 feliratkozónk van a „hírek” témára. Az előfizetők egyenletesen oszlanak el 50000 szerver között. Ennek eredményeként minden frissítés, amely a cserepontra érkezik, 10000 XNUMX-szer replikálódik: XNUMX XNUMX-szer minden szerveren, a rajta lévő előfizetők számának megfelelően. Nem túl hatékony rendszer, igaz?
A helyzet javítása érdekében vezessünk be egy proxyt, amelynek neve megegyezik a csereponttal. A globális névregiszternek tudnia kell a legközelebbi folyamatot név szerint visszaadni, ez fontos.

Indítsuk el ezt a proxyt a hozzáférési réteg szerverein, és a websocket api-t kiszolgáló összes folyamatunk erre fog előfizetni, és nem a kernel eredeti pub-sub cserepontjára. A Proxy csak egyedi előfizetés esetén fizet elő a magra, és replikálja a bejövő üzenetet minden előfizetőjének.
Ennek eredményeként 5 50000 helyett XNUMX üzenet kerül elküldésre a kernel és a hozzáférési szerverek között.

Útválasztás és egyensúlyozás

Req-Resp

A jelenlegi üzenetküldési megvalósításban 7 kéréselosztási stratégia létezik:

  • default. A kérést minden vezérlőnek elküldjük.
  • round-robin. A kérések felsorolva vannak, és ciklikusan elosztják a vezérlők között.
  • consensus. A szolgálatot kiszolgáló irányítókat vezetőkre és rabszolgákra osztják. A kéréseket csak a vezetőnek küldjük el.
  • consensus & round-robin. A csoportnak van vezetője, de a kéréseket minden tag között elosztják.
  • sticky. A hash függvény kiszámítása és hozzárendelése egy adott kezelőhöz. Az ezt az aláírást tartalmazó kérések ugyanahhoz a kezelőhöz érkeznek.
  • sticky-fun. A cserepont inicializálása során a hash számítási függvény a sticky egyensúlyozás.
  • fun. A ragadós mókához hasonlóan csak Ön tudja tovább irányítani, elutasítani vagy elődolgozni.

Az elosztási stratégia a cserepont inicializálásakor kerül beállításra.

A kiegyenlítés mellett az üzenetküldés lehetővé teszi az entitások címkézését. Nézzük meg a rendszerben található címkék típusait:

  • Csatlakozási címke. Lehetővé teszi, hogy megértse, milyen kapcsolaton keresztül jöttek az események. Akkor használatos, ha egy vezérlőfolyamat ugyanahhoz a csereponthoz csatlakozik, de különböző útválasztási kulcsokkal.
  • Szervizcímke. Lehetővé teszi a kezelők csoportokba való egyesítését egy szolgáltatáshoz, valamint az útválasztási és kiegyensúlyozási képességek bővítését. A req-resp minta esetében az útválasztás lineáris. Kérést küldünk a cserepontnak, majd az továbbítja a szerviznek. De ha logikai csoportokra kell osztanunk a kezelőket, akkor a felosztás címkék segítségével történik. Címke megadásakor a kérés a vezérlők meghatározott csoportjához kerül elküldésre.
  • Címke kérése. Lehetővé teszi a válaszok megkülönböztetését. Mivel rendszerünk aszinkron, a szolgáltatási válaszok feldolgozásához meg kell tudnunk adni egy RequestTag-et a kérés elküldésekor. Ebből meg fogjuk érteni azt a választ, hogy melyik kérés érkezett hozzánk.

Pub-sub

A pub-sub esetében minden egy kicsit egyszerűbb. Van egy cserepontunk, ahová az üzeneteket közzétesszük. A cserepont elosztja az üzeneteket azon előfizetők között, akik előfizettek a számukra szükséges útválasztási kulcsokra (mondhatjuk, hogy ez analóg a témákkal).

Skálázhatóság és hibatűrés

A rendszer egészének skálázhatósága a rendszer rétegeinek és összetevőinek skálázhatóságától függ:

  • A szolgáltatások méretezhetők további csomópontok hozzáadásával a fürthöz a szolgáltatás kezelőivel. A próbaüzem során kiválaszthatja az optimális egyensúlyi politikát.
  • Maga az üzenetküldő szolgáltatás egy külön fürtön belül általában úgy van méretezve, hogy a különösen terhelt cserepontokat külön fürtcsomópontokra helyezi át, vagy úgy, hogy proxyfolyamatokat ad hozzá a fürt különösen terhelt területeihez.
  • A teljes rendszer skálázhatósága, mint jellemző, az architektúra rugalmasságától és az egyes klaszterek közös logikai entitássá történő kombinálásának képességétől függ.

Egy projekt sikere gyakran a méretezés egyszerűségétől és gyorsaságától függ. Az üzenetküldés jelenlegi verziójában az alkalmazással együtt növekszik. Ha hiányzik is egy 50-60 gépből álló klaszter, akkor is folyamodhatunk a föderációhoz. Sajnos a szövetség témája túlmutat e cikk keretein.

Foglalás

A terheléselosztás elemzésénél már szó volt a szervizvezérlők redundanciájáról. Az üzenetküldést azonban le kell foglalni. Csomópont vagy gép összeomlása esetén az üzenetküldésnek automatikusan helyre kell állnia, mégpedig a lehető legrövidebb időn belül.

Projektjeimben további csomópontokat használok, amelyek esés esetén felveszik a terhelést. Az Erlang szabványos elosztott módú megvalósítással rendelkezik az OTP-alkalmazásokhoz. Az elosztott mód hiba esetén a meghibásodott alkalmazást egy másik, korábban elindított csomóponton indítja el a helyreállítást. A folyamat átlátható, hiba után az alkalmazás automatikusan átkerül a feladatátvételi csomópontra. Erről a funkcióról bővebben olvashat itt.

termelékenység

Próbáljuk meg legalább nagyjából összehasonlítani a rabbitmq és az egyéni üzenetküldésünk teljesítményét.
találtam hivatalos eredmények rabbitmq tesztelés az openstack csapattól.

A 6.14.1.2.1.2.2. Az eredeti dokumentum az RPC CAST eredményét mutatja:
Az elosztott alkalmazások építőkövei. Második közelítés

Nem adunk meg előzetesen további beállításokat az operációs rendszer kernelén vagy az erlang virtuális gépen. A tesztelés feltételei:

  • erl opt: +A1 +sbtu.
  • Az egyetlen erlang csomóponton belüli tesztet egy régi i7 mobil verziójú laptopon futtatják.
  • A klaszterteszteket 10G hálózattal rendelkező szervereken végzik.
  • A kód docker-tárolókban fut. Hálózat NAT módban.

Teszt kód:

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.

1. forgatókönyv: A teszt egy régi i7 mobilverzióval rendelkező laptopon fut. A teszt, az üzenetküldés és a szolgáltatás egy Docker-tároló egy csomópontján fut:

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)

2. forgatókönyv: 3 csomópont fut különböző gépeken docker (NAT) alatt.

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)

A CPU kihasználtsága minden esetben nem haladta meg a 250%-ot

Eredményei

Remélem, ez a ciklus nem tűnik elmelerakásnak, és tapasztalataim valóban hasznosak lesznek mind az elosztott rendszerek kutatói, mind a gyakorlati szakemberek számára, akik még az elején járnak az elosztott architektúrák kiépítésében üzleti rendszereik számára, és érdeklődéssel nézik az Erlang/Elixirt. , de kételkedj abban, hogy megéri-e...

fénykép @chuttersnap

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Milyen témákkal érdemes részletesebben foglalkoznom a VTrade Experiment sorozat részeként?

  • Elmélet: Piacok, megbízások és időzítésük: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Megrendelések könyve. Egy könyv csoportosítással való megvalósításának elmélete és gyakorlata

  • A kereskedés vizualizálása: Tickek, sávok, határozatok. Hogyan kell tárolni és hogyan kell ragasztani

  • Hátsó iroda. Tervezés és fejlesztés. Munkavállalók megfigyelése és kivizsgálása

  • API. Nézzük meg, milyen interfészekre van szükség, és hogyan valósítsuk meg őket

  • Információ tárolása: PostgreSQL, Timescale, Tarantool kereskedési rendszerekben

  • Reaktivitás a kereskedési rendszerekben

  • Egyéb. Megírom a kommentekben

6 felhasználó szavazott. 4 felhasználó tartózkodott.

Forrás: will.com

Hozzászólás