Kontextus

A közelmúltban a Jupiter elindította ügyfelei számára a befektetési alap platformjának tervezési kezdeményezését. Ez lehetőséget adott arra, hogy újraértékeljük azokat a technológiákat, amelyekre a Jupiter eddig támaszkodott, és azok teljesítményét a méretekben.

Ezek a legfontosabb szempontok, amelyeket meg kell tenni -

  1. Keret/technológiai választások
  2. Kódolási paradigmák, minták

A fenti szakaszok mindegyikénél újraértékelésre volt szükség annak ellenőrzésére, hogy a rendszerek mennyire méretezhetőek. Ez a gyakorlat abban is segített, hogy kitaláljuk rendszereink korlátait, és azt, hogy mennyire skálázódnak.

Feltételezések

  1. A közönség tisztában van a mikroszolgáltatás-architektúrával és az általa a monolitikus kóddal szemben nyújtott általános előnyökkel.
  2. A közönség elsősorban a JVM nyelvek bármelyikével dolgozik[link]

Előfeltételes kérdések

A funkcionális követelmények mellett a következő értékeléseket kell elvégezni.
Ezek a) csoportértékelést és b) projektértékelést egyaránt tartalmaznak -

  1. Mi a csapat alapvető szakértelme/tudásbázisa? Melyik nyelvet/keretrendszert ismeri a csapat?
  2. Van-e elegendő emberünk, sokrétű tudással és szakértelemmel, hogy elkerüljük a visszhangkamrát?
  3. Mennyi időbe telik a csapatnak, hogy új kereteket/technológiákat hozzon létre?
  4. Milyen méretarányra tervezik a rendszert?
  5. Mi az az idővonal, amelyet a termék elképzel? Melyek a mérnöki POV hozzávetőleges idővonalai [beleértve a műszaki értékeléseket, a POC-kat és a megfontolásokat]?

Ezeket a kérdéseket előzetesen átgondolva megbizonyosodhatunk arról, hogy a projekt nem lesz „BigBallOfMud”.

Keret/technológiai választások

Rómát sem egy nap alatt építették.

Itt is két alternatívát javasolunk, illetve ezek felhasználását különböző szolgáltatásokban.

Összehasonlítás

Miért? 🤔

Micronaut vs Spring-Boot

A Micronaut natívan felhőszolgáltatásokhoz készült, fordítási időben függőségi injekciót végez, és 50%-kal kevesebb rendszerindítási időt és memóriát igényel [a tavaszi rendszerindításhoz képest][link]

Kotlin vs Java

A Kotlin kevesebb kódsort igényel + könnyebb karbantartani és elfogadni + jó támogatást nyújt[link][link]

Feign vs Webflux vs Micronaut

A Feign natívan nem támogatja a reaktív programozást, és minden API-hívást blokkoló módon hajt végre. Ez azt eredményezi, hogy a szolgáltatás lefagy [elfogy a szálak] egy downstream szolgáltatás időkorlátja esetén.

A Webflux támogatja a reaktív működést, és jól integrálható az OpenAPI generátorral, így nincs szükség sok kazánlemez kódra.

A Micronaut alapértelmezés szerint felhőszolgáltatásokhoz készült, és nem igényel harmadik féltől származó API-klienseket

jOOQ, R2DBC és KMongo

A jOOQ R2DBC támogatás biztosítja, hogy a DB i/o természeténél fogva nem blokkoló legyen[link]

A MongoDB esetében a KMongo[link] azonnali objektum-leképezést biztosít, és típusbiztosítja a lekérdezéseket[link]

Sémaáttelepítés [TBD]

A MongoDB egy dokumentumadatbázis, és nem igényel séma-migrációt [nincs rögzített séma, amelyhez a gyűjtemény ragaszkodik]

Indexek és új gyűjtemények létrehozása esetén a Mongock[link] egy lehetőség. Alternatív megoldásként a lambda-nyomtatásban is gondolkodunk az esetleges ad hoc változtatásokhoz.

OpenAPI generátor

Az OpenAPI generátor megkíméli a csapatot attól, hogy sok kazánkódot írjon a szolgáltatáshoz szükséges kliensekhez/modellekhez.
Sajnos a Micronaut-ot jelenleg nem támogatja az OpenAPI Generator (béta)[link]. Ezért egyelőre az OpenAPI YAML specifikációt állítjuk elő a micronaut megjegyzések alapján.[link]

A következőre konvertálva:

Postgres vs MongoDB

A Postgres nem egy természetesen szilánkos adatbázis. Partícionálást biztosít, ami segít az indexek kisebb méretében, és gyorsabbá teszi a keresést.

Avégtelen léptékkel azonban végtelen problémák jönnek. Ha az adatok olyan mértékben felduzzadnak, hogy egyetlen szerver nem tudja feldolgozni azokat, még a particionálás is nehezen kezelhető.

A NoSQL segít a vízszintes skálázásban azáltal, hogy a táblákat természetes módon szilánkokra bonthatóvá teszi, csupán egy szilánkos kulcs meghatározásával. Ha egy alkalmazás hozzáférési mintái nem túl bonyolultak [azaz nem igényel túl sok csatlakozást, vagy nem függ az adatbázis triggerektől + tranzakcióktól], a dokumentum-adatbázisok gyorsabb kulcs-érték hozzáférést biztosítanak, és gyakorlatilag végtelenül skálázhatók.

Egyéb megfontolt lehetőségek: DynamoDB, ScyllaDB, Cassandra

Gyorsítótár

A Redis elsősorban az időtesztelt gyorsítótárazási rétegként szolgált, amely magából a gyorsítótárrétegből kiszolgálja a későbbi és ismétlődő felhasználói lekérdezéseket.

A Mongo-val azt tervezzük, hogy kibővítjük a használati esetet, hogy adattárként is működjön néhány gyors hozzáférésű adat számára, és ezáltal a rendszer kevésbé függjön az indexektől.

Például az összes felhasználói tranzakcióazonosítót időbélyegük szerint rendezve tároljuk a redis-ben, így a szeletelés és a kockázás magán a gyorsítótár-adatokon történhet, és a DB-t nem kell lekérdezni minden felhasználói kérésnél.

A DB elsősorban egy célt fog szolgálni – egy rekordazonosítóval lekéri a rekord részleteit [legyen szó felhasználókról, tranzakciókról, rendelésekről stb.]

Paradigmák és minták kódolása

Két dologra lehet elsősorban összpontosítani:

  1. Algoritmusok
  2. Hozzáférési minták

A probléma ezen részéhez nagyon sok forrás áll rendelkezésre, ezért csak a legfontosabb szempontokat említem meg.

  1. Tartsa be a SOLID alapelveit [link]
  2. Kerülje a már ismert dolgok újraszámítását/újbóli lehívását. Ha az adatok nem változnak gyakran, több helyen gyorsítótárazhatók [kliensoldali, CDN, szerveroldali, DB gyors hozzáférési réteg stb.]
  3. Kerülje az idő előtti optimalizálást [link]
  4. Tartsa szem előtt az általános felhasználói élményt

TL;DR

  1. Először vetítse ki a felhasználói forgalmat a rendszerére. Elemezze, hogy a következő 2 évben mekkora lesz a várható forgalom az alkalmazáson, és próbálja meg legalább 10-szeresét biztosítani. [Ez lehet az aktuális forgalom egyszerű kivetítése].
  2. A felhasználói forgalom alapján próbálja kitalálni a szűk keresztmetszeteket. Van olyan lekérdezés, amelynek futtatása túl sokáig tart, vagy sok adatbázis-számítást használ? Optimalizálható a lekérdezés? A lekérdezhető paraméterek indexelhetők, ha még nem?
  3. Ismerje meg rendszerének korlátait. A kód monolitikus, vagy van más egyetlen hibapontja? Az architektúrának vannak eredendő korlátai, például túl sok függőség és blokkoló hívás? Hogyan lehet ezt minimális változtatásokkal újra felépíteni?
  4. NE HAJTJA újra a kódot, hacsak nem feltétlenül szükséges. Ne optimalizálja túl, és kerülje el, hogy más mérnökök életét sokkal bonyolultabbá tegye a projekt jövőjében.