Rust tootmiskeskkonnas: Kuidas me kirjutasime fintech API ümber ja vähendasime AWS kulusid 70%
Reaalne ülevaade sellest, kuidas me kirjutasime kliendi Node.js maksete valideerimise teenuse Rustis ümber — ja numbrid, mis sellele järgnesid: p99 latentsus langes 45ms-lt 3ms-le, mälukasutus 512MB-lt 48MB-le konteineri kohta ning igakuine AWS arve vähenes 70%.
- aasta lõpus pöördus üks meie Eesti fintech klientidest meie poole probleemiga, mis kõlab paljudele arendusmeeskondadele tuttavalt: nende Node.js maksete valideerimise teenus muutus ühelt poolt kalliks käitada ja teiselt poolt raskeks skaleerida tippkoormuse ajal. Tehingute mahud olid kahe aasta jooksul kasvanud 4 korda ja infrastruktuurikulud olid vastavalt kasvanud — ilma proportsionaalse jõudluse paranemiseta.
Me pakkusime välja täieliku ümbertöötluse Rustis. Klient oli skeptiline — Rustil on maine järsu õppimiskõveraga keelena, mis on aeglase arenduskiirusega. See postitus on aus ülevaade sellest, mis juhtus: mida me ehitasime, mida leidsime ja millised numbrid olid kuus kuud hiljem tootmiskeskkonnas.
Lähtepunkt: Node.js valideerimisteenus
Kõnealune teenus käsitles maksete valideerimist — kontrollides tehingupiiranguid, petturisignaale, kaupmehe reegleid ja kontojäägi eelkontrolle enne makse töötlejale saatmist. See töötas tippkoormusel ligikaudu 2000 päringut sekundis, rangete SLA nõuetega: p99 latentsus alla 50ms, tööaeg 99,99%.
Node.js implementatsioon oli neli aastat orgaaniliselt arenenud. See töötas, kuid näitas oma vanust:
- Mälukasutus: Iga eksemplar tarvis 512MB puhkeolekus, suurenedes koormusel 900MB-ni. 12 eksemplari käitamine redundantsuse jaoks maksis EC2 pealt ainuüksi ~1400 dollarit kuus.
- GC pausid: V8 prügikoguja põhjustas ettearvamatud latentsuspiigid — p99 oli tavaliselt ligikaudu 45ms, kuid tõusis mitu korda päevas üle 200ms.
- CPU koormus: Üksik Node.js töötaja sai hallata ~300 päringut sekundis enne küllastumist. Nad vajasid 8 töötajat eksemplari kohta.
Ümbertöötlus: Axum + Tokio stabiilsel Rustil
Kasutasime kaks nädalat uurimistöös — kaardistades iga valideerimisreegli, iga erijuhtu, iga veakoodi, mida olemasolev teenus tootis. See oli möödapääsmatu: käitumispariteeti pidi olema 100% enne liikluse lülitamist.
Uus pakk:
- Axum (0.8) HTTP raamistikuna — ergonoomilne, async-native, suurepärane Tower vahevara ökosüsteem
- Tokio asünkroonse käituskeskkonnana — lahingutestitua, ettearvatavateed jõudlusomadused
- sqlx andmebaasipääsuks — kompileerimisajal kontrollitud SQL päringud, nullpõllulised üllatused käitusajal
- serde + serde_json serialiseerimiseks — tõhus deserialiseerimine valideerimiskoormustest
Tulemused kuue kuu pärast
Latentsus:
- p50: 0,8ms (oli 8ms) — 90% vähendamine
- p99: 3ms (oli 45ms) — 93% vähendamine
- p99,9: 7ms (oli 200ms+) — 97%+ vähendamine
Mälukasutus:
- Puhkeolekus: 48MB eksemplari kohta (oli 512MB) — 91% vähendamine
- Tippkoormusel: 72MB eksemplari kohta (oli 900MB) — 92% vähendamine
Kulu:
- EC2 arvutus (ainult maksete valideerimine): 420 dollarit kuus (oli 1400 dollarit) — 70% vähendamine
Ausad kompromissid
Rust ei ole iga teenuse jaoks õige valik. Siin on see, mida me klientidele ausalt ütleme:
- Suurem esialgne insenerikulu. Rusti arendus võtab Node.js-iga võrreldes kauem aega keeles uute inseneride jaoks.
- Väiksem tööjõu turg. Rusti insenereid on vähem kui Node.js insenereid.
- Aeglasem iteratsioon mittekritiilistele teenustele. Lihtsa CRUD API jaoks minimaalsete jõudlusnõuetega on Rusti tüübisüsteemi koormus väiksema tasuvusega.
Valem, mida me kasutame: kui teenus käsitleb üle 500 päringu sekundis, omab rangeid latentsuse SLA-sid (p99 < 20ms) või töötab püsivalt tootmises kõrge mälueraldusega — siis Rust tasub end ära 6–12 kuuga pilvekulude kokkuhoiust.
Tootmise jõudlustulemused
* Tulemused 6 kuu tootmistöötlusest. Klient anonümiseeritud kokkuleppel.