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.
Ez az Erlang/Elixir elosztott reaktív alkalmazásairól szóló sorozat utolsó cikke. BAN BEN
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:
- most a szolgáltatásnak 5 fürtcsomóponton kell feldolgoznia a kéréseket,
- képes legyen háttérfeldolgozási feladatok elvégzésére,
- é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 asticky
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
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
A 6.14.1.2.1.2.2. Az eredeti dokumentum az RPC CAST eredményét mutatja:
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
A felmérésben csak regisztrált felhasználók vehetnek részt.
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