[Videó] Microservices, avagy a monolit már a múlté?

Címkék

A microservice architektúra népszerűvé válásával sokszor úgy tűnik, mintha a monolitikus architektúra mára elavulttá vált volna. Szabó Dávid (LeanNet) alább megtekinthető előadásában arra próbált meg válaszolni, vajon tényleg a múlté-e a klasszikus, monolitikus architektúra. Ehhez először áttekintjük, hogy mit is értünk a két architekturális stílus alatt, azok jellemző előnyeivel és hátrányaival, majd megvizsgáljuk, hogy egy termék fejlesztési folyamatát tekintve milyen megfontolásokkal érdemes belevágni a microservice-ekbe.

Ha érdekel a téma, gyere el a HWSW november 23-án kezdődő, 6 alkalmas, 12 órás, "A microservice architektúra alapjai" című online képzésére, ahol Dávid vezetésével tanulhatsz.

Hozzászólások

Szerkesztve: 2021. 10. 18., h – 12:53

Ezekkel a mikroszerviszekkel újra felfedezték az osztályokat meg az interfészeket. Csak most már virtuális függvény/metódus hívás helyett http kérés van, mert az mondjuk nagyjából 10-100 ezerszer lassabb (saccolom, nem is gondoltam végig, valaki tudja ezt számszakilag pontosabban?). És sokkal bonyolultabb megvalósítani is (amit nagyon helyesen megemlített az előadó is). Megoldás a túl gyors hardverre és a túl sok fejlesztőre is. A legjobb az benne, hogy ezzel a megoldással nem csak lehet skálázni, de muszáj is, mert ami monolitikusan elketyegne 1 vason, ahhoz így fog kelleni vagy 10.

Hát na... A fejlődésben elértünk oda, hogy ugyanazon feladatra, régen, lassabb gépeken jobban működő megoldások születtek, mint manapság, ahol a hardverek teljesítményének nem kellene gondot okoznia, mégis lassabb és több erőforrást igénylő dolgokat készítenek.

 

Manapság kezdik belátni, hogy ami régen jól működött, azt megpróbálják újra implementálni, viszont ennek megvalósítására is olyan eszközöket használnak, amik már nem alkalmasak ezek kiszolgálására, tehát ahelyett, hogy javulna bármi is, ugyanott vagyunk, hogy rosszabb a teljesítmény, mint régen.

"Errors are red
My screen in blue
Someone help me
I've deleted Sys32"

Kicsit. Kommentben nem akartam teljes architekturális tanácsadást folytatni, az csak pénzér jár :-)

De egyet mégis mondok: ha lehet spórolj meg mennél több réteget, mennél több kommunikációt és mennél több buzzword-öt!

Egyébként az előadás ebből a szempontból elég korrekt, mert ő is azt mondja, hogy a legtöbb esetben nincs szükséged mikroszervízre, és ha van, akkor is fokozatosan kell bevezetni ott kezdve, ahol valós skálázódási probléma merül fel például. Ezzel még nincs is bajom, jól csinálva van értelme.

De ez is egy buzzword, amit úgy hallottam használni (közelről még nem láttam ilyen állatot hála a jó égnek), hogy bemondta valaki, hogy ezt kell csinálni, beleöntöttek egy rakás pénzt és időt, és a vége az lett, hogy minden baromi lassú lett a távoli hívás, parszolás és sorosítás miatt, és rohadt bonyolult bármit megváltoztatni, mert 4-5 csapattal kell egyeztetni az API változásokat.

Ezekkel a mikroszerviszekkel újra felfedezték az osztályokat meg az interfészeket. Csak most már virtuális függvény/metódus hívás helyett http kérés van, mert az mondjuk nagyjából 10-100 ezerszer lassabb

Ha a kiinduló feltételezésed igaz lenne, akkor a konklúzió is igaz lenne.

A mikroszervizek nem az osztályok és interfészek megfelelői és nem is a függvény/metódus hívás helyett vannak a http kérések.

Ha lassú/gyors egy oldal, az nem függ attól, hogy MS vagy Monolit. Lehet lassú monolitot és villámgyors MS-t is írni, ahogy fordítva is.

> Ha lassú/gyors egy oldal, az nem függ attól, hogy MS vagy Monolit. Lehet lassú monolitot és villámgyors MS-t is írni, ahogy fordítva is.

Nem csak attól függ, nyilván kicsit túloztam. De a remote metódushívás (vagy nevezzük aminek akarjuk, nem az a lényeg, hanem hogy interprocessz kommunikáció) mindenképpen időbe és CPU-ba fog kerülni. Ingyen biztosan nincsen ilyen.

Teljesen ugyanaz az implementáció monolitikusként megvalósítva az elosztott megvalósítás idejéig fog futni, mínusz a kommunikációra fordított idő. Tehát gyorsabban. Nyilván ez addig igaz, ameddig egyáltalán működőképes a monolitikus megvalósítás, tehát nem lesz szükséges valami okból elosztott rendszert építeni, mert nem tud minden egy vason futni.

> A mikroszervizek nem az osztályok és interfészek megfelelői és nem is a függvény/metódus hívás helyett vannak a http kérések.

Ha monolitként valósítod meg ugyanazt a funkcionalitást, akkor mégis mi volna az, ami mikroszervizekkel valami távoli metódushívás lesz? (Távoli metódushívás: pl valami RMI HTTP felett, de nyilván bármi más is lehet, a lényeg, hogy logikailag interprocessz metódushívás.) Én úgy csinálnám, hogy a funkcionális blokkok valamiféle interfészek szerint kommunikálnának 1 vagy több osztályban vagy interfészen volna megfogalmazva, hogy mit csinálnak egymással. Tehát távoli metódushívás helyett helyi metódushívás volna.

Ha jol gondolom arra akart ramutatni, hogy monolit architektura (vagy annak hianya) eseten a programozok konnyebben hoznak letre fuggosegeket olyan metodusokra/valtozokra/osztalyokra, ami nem az adott komponens feladatkorebe tartozik.

Peldakent fel tudom hozni az instanceOf() fuggvenyt, es annak altalanos hasznalatat, mint code smell-t :)

 

Szvsz egy jo programozo kepes arra, hogy kulonvalassza a modulokat annyira, hogy azok ne fuggjenek egymastol, csak interfeszeken keresztul. Onnantol kezdve meg tok mindegy, hogy az implementacio egy masik osztalyt, vagy a vilag masik vegen levo szervert hivja meg (A kod szempontjabol nezve).

Lambda calculus puts the fun into functional programming

Nem, még a magas szintű követelmények sem feltétlen ugyanazok. Pl. MS környezetben nem igazán lehet megkövetelni az erős konzisztenciát, ott csak az eventual konzisztenciát (majd egyszer csak konzisztens lesz) lehet elvárni. Ha elvárod, akkor mérhetetlenül lassú lehet a rendszer.

Monolit megoldásnál gyakori, hogy egy oldalon megadsz minden adatot, majd egy gombnyomásra validálja az összeset, majd feldolgozza mindet.
MS rendszereknél, ha ugyanígy lenne megoldva, akkor mondjuk lehetne olyan, hogy 10-20 MS-t is érint a sok adat, így egymást hívogatnák, várnák az eredményeket és jóval lassabb lenne. Ehelyett inkább úgy oldják meg, hogy egy vagy pár adat megadása után már egyből megy az adott MS-hez, ott validálódik és dolgozódik fel egy pillanat alatt. A felhasználó mire a következő adatot adja meg már végzett is az előzővel. A végső gombnyomásnál már csak ellenőrizni kell, hogy minden adat megérkezett, feldolgozódott és nem volt közöttük ütközés.

Se a monolit, se az MS nem gyorsabb a másiknál. Vannak feladatok és azok megoldásai, amelyiknél az egyik lehet gyorsabb, és vannak olyanok, amelyiknél a másik.

De ha ugyanezt a specifikációt monolittal valósítom meg, akkor az úgy lassabb lesz?

Arra akarok kilyukadni, hogy semmi nincsen a mikroszerviz architektúrában, amitől konkrétan bármi gyorsabb lenne. Ha gyorsabb lesz, akkor az azért van, mert másik feladatot oldunk meg. De ha ezt a másik feladatot monolitikusan oldanánk meg, akkor az ugyanúgy gyorsabb lesz, csak még egy kicsivel annál is gyorsabb, mert kevesebb az IPC kommunikáció. Viszont van benne, amitől lassabb lesz, és ez elkerülhetetlen. Nyilván nem tragédia, együtt lehet élni vele, de ez tény.

Az az ötlet, hogy a mikroszervizek külön processzek legyenek teljesen értelmetlen bonyolítás azokat az eseteket kivéve, amikor ennek valami természetes oka van akár a projekt szervezése miatt, akár devops miatt.

Tegyük fel, hogy pontosan ugyanúgy csinálsz mindent a monolitnál is:

  • nagyon pici, független modulok,
  • minden modulnak saját adatbázisa van,
  • minden modulnál el kell érni, hogy tetszőlegesen sok példányban tudjanak futni egymás mellett,
  • minden modul kívülről elérhető egy API-n (pl. REST, MessageQueue) keresztül,
  • ...

Ilyenkor egyrészt kérdéses, hogy még mindig monolitról van-e szó, de legyen az. Ekkor vélhetően a legtöbb esetben valamivel gyorsabb lehet a monolit.

Az egyedüli kivétel az lehet, ha beborul egy modul és magával rántja az egész monolitot, ilyenkor MS esetben az az egy pici MS-nek egy példánya hal meg, ami helyett indít szinte azonnal egy újat, de addig is, esetlegesen csak egy pici része nem fog menni,  vagy lassabban az alkalmazásnak.

Egyébként lehet ötvözni a kettőt, én is csináltam olyan hobbi MS rendszert, amit lehet kvázi monolitként is futtatni.

Monolit esetén általában egy nagy adatbázisban tárolnak minden adatot, így ha sok eltérő "modult" érintő adatírás van, akkor azt könnyedén tranzakcióban, konzisztensen lehet tárolni.

MS esetén általában minden egyes MS-nek saját adatbázisa van, így több MS-t érintő adatírást nem igazan lehet konzisztensen megoldani, csak úgynevezett eventual konzisztensen.

microservicesk azert vannak, hogy hibat konyebben lehesen localizalni.
Ha monolit el crashel vagy leakel akkor tul nagy reszt kell megnezni.

Valoban lassabb, de nem itt bukik meg a a service teljesitmenye.
Ha rendes native codot hasznalsz (pl. rust)  akkor meg mindig ki lehet szolgalni a feladatott ertelmesen.

TCP_RR regen 30 usec korul mozgott egy ~2GHZ -s serveren. manapsag localhost masik utat hasznal (volt hogy localhost roszabb volt, gyors interface eseten).
Namarmost 1ms latency egy web servertol nem rosz, ha -e kozben csinal 10 parhuzmos hivast, ami mondjuk ugrik meg vagy ketot akkor meg idon belul vagyunk.

microservicel ugy todod labon loni magad hogyha rosz a terv es tul sok tavoli hivas kell, vagy kozistenciat sertenek sorozatban. 
Aminek egy tranakcinak kene lenie de micro orultseg miett nem megy , akkor baj lehet.

Ha mar hivas koltsegnel vagyunk, egy interpretalt osztalyokat kezelo cumonal hanyszor nagyobb egy methodus hivas koltsege C++ -hoz kepest ;-)
Hint: sok esetben itt tobet buksz mint par tavoli hovasnal, mert egysek sok-sok methoduson keresztul szeretnek nagyon trivialis dolgokat is csinalni.

Webes cuccok altalaban nagyon nem hatekonyak, ha nem microservicek miatt, akkor is valami mindig bekerul amitol total zaba gep lesz,
pedig nem volna nehez rendesen csinalni ..
 

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

A legfőbb probléma szerintem a monolit alkalmazásnál (és teljesen mindegy hogy simán csak monolitikus, vagy moduláris monolit) az az egy/közös adatbázis. Ennek az az oka, hogy a különböző fogalmakból leképzett nézeteket (melyek az egyes aktoroknak kellenek) összerakják egy táblába, és nagyon hamar egy rohadt nagy spagetti struktúra lesz belőle, amiből persze mindent ki lehet navigálni, de semmit nem lehet rajta változtatni, mert senki nem tudja, hogy ezzel milyen üzleti funkciót fog eltörni, ráadásul egy új üzleti funkciónál az összes domain-t az embernek a fejében kell tartani, ami egy adott méret felett (és ez nem is olyan hatalmas mint azt sokan gondolják) totálisan reménytelen.

Ez szerintem az egyik fő oka annak, hogy egy adott idő után teljesen karbantarthatatlan lesz a monolitikus alkalmazás, szépen felmondanak/kiégnek a fejlesztők, és ha jelentkezik is valaki, azt fogja mondani, hogy ezt a szart írjuk már újra, mert így nem lehet üzleti funkciókat megvalósítani, sosem lesznek kész.

Ha viszont szétszeded az adatbázist, akkor magadra húzol mindent, amit enpassant írt. Valamit valamiért.

Neked lehet hogy nem, de ha azt mondom, hogy ha kitörlöm ezt az oszlopot, akkor melyik üzleti folyamatot fogjuk eltörni, akkor arra valószínűleg semmit nem tudnál mondani egy bármilyen értelmes méretű kódbázisban, mert egyszerűen nem lehet impact analízist csinálni. Pont ez az oka annak, hogy senki nem akarja ezeket a legacy appokat még bottal sem piszkálni. Ahhoz, hogy beletanuljon bárki, évek kellenek, egy átlag fejlesztőnek ehhez se kedve, se ideje nincs (max 2 évig vannak egy cégnél). Egy microservice architektúrában ott van a kis adatbázisa ami egy adott subdomain-hez kell, néhány 10 tábla plusz a hozzá kapcsolódó üzleti folyamatok, ráadásul interfészen keresztül kommunikál, ezt még képesek felfogni, és ezen belül viszonylag hamar "termelni".

Egyetértek a cikk mondanivalójával.

Nem is biztos, hogy ellentmond a kettő egyébként, mert a videó is azt mondja, és ennek is az a konklúziója, hogy egy bizonyos méret felett van értelme, az alatt meg nincsen. Amiért ez a cikk nagyon jogos az az, hogy túlzottan divatos lett a mikroszerviz és az is használja, akinek nem kellene.

Bizonyos meret felett microservice orulet elott sem voltak csak monolitok.
 - contenet mangement interface
 - user frontend
 - bacend for frontend from content
 - reverse proxy cache
 - loadablanacer
 - http frontend
 - static content server
 - adatbazis(ok)
 - log handler
 - search enginn
 - cache grid
 - message queue
 - sso

A Kerdes az , hogy meg ezt is microznad -e ?

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

hup uzleti logikalyat hagy kontenernek kene kiszolgalnia hany adat bazissal ?

 - forum
 - blogok
 - cikk iras
 - cikk publikalas (content lathatova tetel)
 - cikkek olvasas
 - porlet nek latszo targyak a szelen
 - rss
 - user kovetes
 - user managment

Mindhez johet egy kulon cache tier layer.
 

Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Ennél nagyobb portáloknál teljesen mindennapi, hogy a maga a tartalom nincs összedrótozva a frontenddel: a szerkesztők gyártják a tartalmat valahogyan, ez bekerül egy CMS-be, és a frontend onnan szedi ki, ami kell neki.

A hupon ennek nem biztos, hogy lenne értelme, mert egyféle workflow van. Ha mondjuk lenne mobilapp a cikkek olvasásához, a userek a hup-hu-ról írnák a saját tartalmakat, de a HWSW mondjuk API-n küldené be a szponzorált tartalmat, akkor lenne értelme darabolni, és csinálni mondjuk egy külön CMS-t és külön frontend engine-t.

Kezdjük ott, hogy milyen aktorok vannak? Ha több aktor van, akkor ők egy adott fogalomnak ugyanazt a nézetét, vagy más nézetét látják? Mennyire kell azonnali konzisztenciának lenni? Ha bárhol meghúzzuk a határokat, vannak-e olyan folyamataink, amelyek átnyúlnak több subdomain-en? Hány ilyen van? Milyen SLA-t kell nekik biztosítani? A rendszer egyes részei milyen terhelést kapnak? Hány folyamatunk van? (itt felsoroltál párat, de nem látjuk, hogy ez a teljes, vagy az 1%-a a teljes üzleti folyamatoknak). Hány fogalmunk van? (lásd előbb)

Plusz a fenti listához: teljesen keveredik a fogalom (pl. blog), a folyamat (cikk írása), a követelmény, és a technikai megvalósítás. Amíg ezeket nem rakjuk rendbe, addig nehéz lesz bármiről is beszélni.

Megfordítom: milyen/hány új üzleti funkció került implementálásra a HUP-on az elmúlt X évben? Hány különböző csoport (sales, marketing, raktározás, számlázás, stb?) használja egyszerre a hup rendszerét? Történik-e bármilyen (üzleti) probléma, ha az egyik részét szétterhelik (lásd még covid oltásra jelentkezés, amire megállt a receptkiadás).

Ha erre a válasz: igazából semmit nem adtunk hozzá az elmúlt egy évben, van három darab rendszergazdánk, aki néha ránéz, és tök mindegy, ha nem megy 2 napig, nincs valójában következménye, akkor nem, semmi értelme nincs microservice alá rakni a hup-ot.

Nem értek hozzá, de azért el tudnék vele indulni. Legyen mondjuk a cikkeket kezelő article microservice.

Első körben azt mondom, hogy a path /article/v1/articles/{id}. A cikket még id nélkül POST-tal lehet beküldeni, olvasni GET-tel, módosítani PUT-tal, publikálást akár PATCH-csel, törölni DELETE-tel lehet. Az összes cikket GET /article/v1/articles?offet=n&limit=m pathon kérheted le (a limitet nem árt limitálni). Idáig minden művelet, ha megfelelő eszközöket használunk, pár sor. De sok problémát nem oldottunk meg.

Ha az emberke profiljában, ami egy másik service meg akarjuk mutatni a cikkeit, akkor SELECT title FROM article WHERE author=userid ORDER BY create DESC; módon meg is van. Csak itt sérül az, hogy az article service a saját db-jének az ura. NoSQL-lel lehet egy külön redundáns táblát erre létrehozni, de akkor is közös a tábla és még munkra húztuk a konzisztencia problémáját, amit batch-csel esetleg lehet orvosolni, ha a db motor támogatja, csak drága. Egy másik lehetőség, hogy a profile service az article service-től kéri el a cikk listát, nyilván nem a teljeset, csak a címet, esetleg create/modify/publish dátumokat is, hogy a user kedvére rendezhesse a böngészőben. Erre kell egy külön API hívás, pár sor csak.

A másik probléma az, hogy honnan tudjuk, hogy a saját cuccát módosítja a felhasználó? Sütiben ott lehet titkosítva a userid, a db alapján látjuk, hogy az övé-e. Az összes nem GET-be ezt bele lehet szenvedni, de valamikor biztosan hibázunk, amit persze egyszerű tesztekkel meg lehetne fogni, ha nem felejtenénk el. Lehetne egy API GW, elborult embereknek sidecar, ami ezt leellenőrzi, persze db nélkül. Na erre kíváncsi vagyok, hogy hogyan csinálnátok meg?

Az első, hogy a saját cikkek listázását, szerkesztését, stb. külön kellene választani az azt a publikum felé kiszolgáló rendszertől. Tök más a funkcionalitása, más a terhelés. A publikálás után a cikk (egy verziója) aszinkron módon, előbb-vagy-utóbb kerül át a sima cikkeket nézegetőknek szánt rendszerbe. Innentől a kettőt leválasztottuk egymástól, még ha ugyan úgy is néz ki, külön lehet őket fejleszteni, új üzleti funkciókat hozzáadni (pl. review).

Azonosítás általában idp-vel (identity provider) történik, ilyen pl. a keycloak, amely beléptet a rendszerbe (egy faktor, több faktor, ubikey, facebook-ra átmappel, stb.), és ad egy token-t. Ez a token igazolja, hogy te ki vagy, és minden egyes endpoint-nál ellenőrzésre kerül hogy te hozzáférhetsz-e az adott erőforráshoz, illetve az adott endpoint-ban futó folyamatnál elérhető lesz kódból az adott folyamatpéldányt aktuálisan végrehajtó felhasználó azonosítója.

Igen, persze, ha kezeljük a verziókat, van review, felelős szerkesztő, stb, akkor nyilván külön kell szedni.

Az authentikáció világos. Az authorizációt nem lehet valahogy kényelmesebben megoldani? Tehát nem csak annyi, hogy az újságírók tudnak POST-ot küldeni x pathra, hanem konkrétan csak a saját cikkjeiket piszkálhassák.

Ami még eszembe jutott, hogy a publikus cikkekről (is) kell valami statisztika. Pl. végigmegyek a cikk tagjein és mindnek növelem a számlálóját. A szerzőnek is pöccintek egyet. A Prometheus majd scrape-eli és ő vagy a Grafana rajzolgat. Nyilván a sok 403, 500 és hasonlókra, ami az API GW-ből remélhetőleg gyűjthető, valami alert is kell. Mire ezt össze copy-paste-eli az ember a StackOverflowról, el is jön az ebédidő.

Az adott microservice el tudja dönteni, hogy az adott cikkíró szerkeszthet-e egy adott cikket. A kérdés, hogy ennek a logikája mennyire bonyolult (ő hozta létre? valami komplex szabály?), és ennek megfelelően kellene eldönteni, hogy ezt hogyan oldja meg az ember. Lehet kézzel ACL-t csinálni, vagy be lehet húzni erre egy megoldást, ez mindig az adott helyzettől függ.

A cikkekről való statisztika megint egy kérdés, alapvetően lehet azt csinálni, hogy amikor létrejön egy cikk, dobunk egy eseményt, amit gyűjtünk, és on-the-fly számolunk egy statisztikai értéket. De ez csak egy megoldása a problémának.