Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Šiame straipsnyje kalbėsiu apie tai, kaip projektas, prie kurio dirbu, iš didelio monolito virto mikropaslaugų rinkiniu.

Projekto istorija prasidėjo gana seniai, 2000 m. pradžioje. Pirmosios versijos buvo parašytos Visual Basic 6. Laikui bėgant tapo aišku, kad plėtrą šia kalba ateityje bus sunku palaikyti, nes IDE o pati kalba prastai išvystyta. 2000-ųjų pabaigoje buvo nuspręsta pereiti prie perspektyvesnės C#. Nauja versija buvo rašoma lygiagrečiai su senosios versijos peržiūra, palaipsniui vis daugiau kodo buvo rašoma .NET. Iš pradžių C# backend buvo orientuotas į paslaugų architektūrą, tačiau kūrimo metu buvo naudojamos bendros bibliotekos su logika, o paslaugos buvo paleistos vienu procesu. Rezultatas buvo programa, kurią pavadinome „paslaugų monolitu“.

Vienas iš nedaugelio šio derinio privalumų buvo paslaugų galimybė skambinti viena kitai per išorinę API. Buvo aiškios prielaidos pereiti prie teisingesnės paslaugos, o ateityje ir mikro paslaugų architektūros.

Dekomponavimo darbus pradėjome apie 2015 m. Idealios būsenos dar nepasiekėme – vis dar yra didelio projekto dalių, kurias vargu ar galima pavadinti monolitais, tačiau jos irgi nepanašios į mikropaslaugas. Nepaisant to, pažanga yra reikšminga.
Apie tai kalbėsiu straipsnyje.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Turinys

Esamo sprendimo architektūra ir problemos


Iš pradžių architektūra atrodė taip: UI yra atskira programa, monolitinė dalis parašyta Visual Basic 6, .NET aplikacija yra susijusių paslaugų rinkinys, dirbantis su gana didele duomenų baze.

Ankstesnio sprendimo trūkumai

Vienas nesėkmės taškas
Turėjome vieną gedimo tašką: .NET programa veikė vienu procesu. Jei kuris nors modulis sugedo, sugedo visa programa ir ją reikėjo paleisti iš naujo. Kadangi automatizuojame daugybę procesų skirtingiems vartotojams, dėl vieno iš jų gedimo visi kurį laiką negalėjo dirbti. O programinės įrangos klaidos atveju net atsarginė kopija nepadėjo.

Patobulinimų eilė
Šis trūkumas yra gana organizacinis. Mūsų programa turi daug klientų, ir jie visi nori ją kuo greičiau patobulinti. Anksčiau to nebuvo įmanoma padaryti lygiagrečiai, o visi klientai stovėdavo eilėje. Šis procesas buvo neigiamas verslui, nes jie turėjo įrodyti, kad jų užduotis yra vertinga. Ir kūrėjų komanda sugaišo laiko tvarkydama šią eilę. Tai užtruko daug laiko ir pastangų, o produktas galiausiai negalėjo pasikeisti taip greitai, kaip norėtųsi.

Neoptimalus išteklių panaudojimas
Priglobdami paslaugas vienu procesu, mes visada visiškai nukopijavome konfigūraciją iš serverio į serverį. Siekdami nešvaistyti išteklių ir lanksčiau valdyti savo diegimo schemą, norėjome atskirai išdėstyti labiausiai apkrautas paslaugas.

Sunku diegti šiuolaikines technologijas
Visiems kūrėjams pažįstama problema: į projektą norima įdiegti modernias technologijas, bet nėra galimybės. Esant dideliam monolitiniam sprendimui, bet koks esamos bibliotekos atnaujinimas, jau nekalbant apie perėjimą prie naujos, virsta gana nebanalia užduotimi. Reikia daug laiko įrodyti komandos vadovui, kad tai atneš daugiau premijų nei išeikvotų nervų.

Sunkumai išduodant pakeitimus
Tai buvo rimčiausia problema – leidimus išleisdavome kas du mėnesius.
Kiekvienas leidimas bankui virto tikra katastrofa, nepaisant kūrėjų bandymų ir pastangų. Verslas suprato, kad savaitės pradžioje kai kurios jo funkcijos neveiks. Ir kūrėjai suprato, kad jų laukia savaitė rimtų incidentų.
Visi turėjo norą pakeisti situaciją.

Lūkesčiai iš mikroservisų


Komponentų problema, kai jie bus paruošti. Paruoštų komponentų pristatymas skaidant tirpalą ir atskiriant skirtingus procesus.

Mažos produktų komandos. Tai svarbu, nes didelę komandą, dirbančią prie senojo monolito, buvo sunku valdyti. Tokia komanda buvo priversta dirbti pagal griežtą procesą, tačiau norėjosi daugiau kūrybiškumo ir savarankiškumo. Tik mažos komandos galėjo tai sau leisti.

Paslaugų išskyrimas atskiruose procesuose. Idealiu atveju norėjau jį izoliuoti konteineriuose, tačiau daug paslaugų, parašytų .NET Framework, veikia tik „Windows“. Dabar pasirodo paslaugos, pagrįstos .NET Core, tačiau jų dar nedaug.

Diegimo lankstumas. Norėtume derinti paslaugas taip, kaip mums to reikia, o ne taip, kaip priverčia kodas.

Naujų technologijų naudojimas. Tai įdomu bet kuriam programuotojui.

Perėjimo problemos


Žinoma, jei monolitą būtų nesunku suskaidyti į mikroservisus, nereikėtų apie tai kalbėti konferencijose ir rašyti straipsnių. Šiame procese yra daug spąstų; aprašysiu pagrindines, kurios mums trukdė.

Pirma problema būdinga daugumai monolitų: verslo logikos darna. Kai rašome monolitą, norime pakartotinai panaudoti savo klases, kad nerašytume nereikalingo kodo. O pereinant prie mikropaslaugų tai tampa problema: visas kodas gana glaudžiai susietas, sunku atskirti paslaugas.

Darbo pradžioje saugykloje buvo daugiau nei 500 projektų ir daugiau nei 700 tūkstančių kodo eilučių. Tai gana didelis sprendimas ir antra problema. Nebuvo įmanoma tiesiog paimti ir suskirstyti į mikropaslaugas.

Trečia problema — būtinos infrastruktūros trūkumas. Tiesą sakant, mes rankiniu būdu nukopijavome šaltinio kodą į serverius.

Kaip pereiti nuo monolito prie mikropaslaugų


Mikropaslaugų teikimas

Pirma, mes iš karto nustatėme, kad mikropaslaugų atskyrimas yra pasikartojantis procesas. Iš mūsų visada buvo reikalaujama lygiagrečiai plėtoti verslo problemas. Kaip mes tai įgyvendinsime techniškai – jau mūsų problema. Todėl mes pasiruošėme iteraciniam procesui. Tai neveiks jokiu kitu būdu, jei turite didelę programą ir ji iš pradžių nėra paruošta perrašyti.

Kokius metodus naudojame mikropaslaugoms izoliuoti?

Pirmasis būdas — perkelti esamus modulius kaip paslaugas. Šiuo atžvilgiu mums pasisekė: jau buvo registruotų paslaugų, kurios veikė naudojant WCF protokolą. Jie buvo suskirstyti į atskirus susirinkimus. Mes juos perkėlėme atskirai, prie kiekvienos konstrukcijos pridėdami nedidelę paleidimo priemonę. Jis buvo parašytas naudojant nuostabią Topshelf biblioteką, kuri leidžia paleisti programą ir kaip paslaugą, ir kaip konsolę. Tai patogu derinant, nes sprendime nereikia papildomų projektų.

Paslaugos buvo sujungtos pagal verslo logiką, nes naudojo bendrus mazgus ir dirbo su bendra duomenų baze. Jų gryna forma vargu ar būtų galima pavadinti mikropaslaugomis. Tačiau šias paslaugas galėtume teikti atskirai, skirtingais procesais. Vien tai leido sumažinti jų įtaką vienas kitam, sumažinant lygiagrečio vystymosi problemą ir vienintelį gedimo tašką.

Surinkimas su pagrindiniu kompiuteriu yra tik viena kodo eilutė Programos klasėje. Paslėpėme darbą su Topshelf pagalbinėje klasėje.

namespace RBA.Services.Accounts.Host
{
   internal class Program
   {
      private static void Main(string[] args)
      {
        HostRunner<Accounts>.Run("RBA.Services.Accounts.Host");

       }
    }
}

Antrasis mikropaslaugų paskirstymo būdas yra: sukurti juos naujoms problemoms spręsti. Jei tuo pačiu metu monolitas neauga, tai jau yra puiku, o tai reiškia, kad judame teisinga kryptimi. Norėdami išspręsti naujas problemas, bandėme sukurti atskiras paslaugas. Jei buvo tokia galimybė, tada sukūrėme daugiau „kanoninių“ paslaugų, kurios visiškai valdo savo duomenų modelį, atskirą duomenų bazę.

Mes, kaip ir daugelis, pradėjome nuo autentifikavimo ir autorizacijos paslaugų. Jie tam puikiai tinka. Jie yra nepriklausomi, kaip taisyklė, turi atskirą duomenų modelį. Jie patys nebendrauja su monolitu, tik jis kreipiasi į juos, kad išspręstų kai kurias problemas. Naudodamiesi šiomis paslaugomis galite pradėti perėjimą prie naujos architektūros, derinti jų infrastruktūrą, išbandyti kai kuriuos metodus, susijusius su tinklo bibliotekomis ir pan. Savo organizacijoje neturime komandos, kuri negalėtų sukurti autentifikavimo paslaugos.

Trečias būdas paskirstyti mikropaslaugasTas, kurį naudojame, yra šiek tiek specifinis mums. Tai verslo logikos pašalinimas iš vartotojo sąsajos sluoksnio. Mūsų pagrindinė vartotojo sąsaja yra darbalaukis; ji, kaip ir užpakalinė programa, parašyta C#. Kūrėjai periodiškai darydavo klaidų ir į vartotojo sąsają perkeldavo logikos dalis, kurios turėjo egzistuoti užpakalinėje programoje ir būti panaudotos pakartotinai.

Jei pažvelgsite į tikrą pavyzdį iš vartotojo sąsajos dalies kodo, pamatysite, kad daugumoje šio sprendimo yra tikros verslo logikos, kuri yra naudinga kituose procesuose, ne tik kuriant vartotojo sąsajos formą.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Tikroji vartotojo sąsajos logika yra tik paskutinėse keliose eilutėse. Perkėlėme jį į serverį, kad jį būtų galima naudoti pakartotinai, taip sumažindami vartotojo sąsają ir pasiekdami tinkamą architektūrą.

Ketvirtasis ir svarbiausias mikropaslaugų izoliavimo būdas, kuris leidžia sumažinti monolitą, yra esamų paslaugų pašalinimas su apdorojimu. Kai išimame esamus modulius tokius, kokie yra, rezultatas ne visada patiks kūrėjams, o verslo procesas gali būti pasenęs nuo tada, kai buvo sukurtas funkcionalumas. Refaktorizuodami galime palaikyti naują verslo procesą, nes verslo reikalavimai nuolat keičiasi. Galime patobulinti šaltinio kodą, pašalinti žinomus defektus ir sukurti geresnį duomenų modelį. Gaunama daug naudos.

Paslaugų atskyrimas nuo apdorojimo yra neatsiejamai susijęs su riboto konteksto samprata. Tai domenu grindžiamo dizaino koncepcija. Tai reiškia domeno modelio skyrių, kuriame visi vienos kalbos terminai yra vienareikšmiškai apibrėžti. Pažvelkime į draudimo ir sąskaitų kontekstą kaip pavyzdį. Turime monolitinę programą ir turime dirbti su sąskaita draudime. Tikimės, kad kūrėjas suras esamą paskyros klasę kitame rinkinyje, nurodys ją iš draudimo klasės ir turėsime veikiantį kodą. Bus laikomasi DRY principo, užduotis bus atlikta greičiau naudojant esamą kodą.

Dėl to išeina, kad sąskaitų ir draudimo kontekstai yra susiję. Atsiradus naujiems reikalavimams, ši sąsaja trukdys plėtrai, todėl sudėtingesnė ir taip sudėtinga verslo logika. Norėdami išspręsti šią problemą, kode turite rasti ribas tarp kontekstų ir pašalinti jų pažeidimus. Pavyzdžiui, draudimo kontekste visiškai įmanoma, kad užteks 20 skaitmenų Centrinio banko sąskaitos numerio ir sąskaitos atidarymo datos.

Norėdami atskirti šiuos ribotus kontekstus vienas nuo kito ir pradėti mikropaslaugų atskyrimo nuo monolitinio sprendimo procesą, naudojome tokį metodą kaip išorinių API kūrimas programoje. Jei žinojome, kad koks nors modulis turėtų tapti mikropaslauga, kažkaip modifikuota proceso metu, tai iš karto iškvietėme logiką, kuri priklauso kitam ribotam kontekstui per išorinius skambučius. Pavyzdžiui, per REST arba WCF.

Tvirtai nusprendėme, kad neišvengsime kodo, kuriam reikalingos paskirstytos operacijos. Mūsų atveju šios taisyklės laikytis pasirodė gana paprasta. Dar nesame susidūrę su situacijomis, kai griežtų paskirstytų sandorių tikrai reikia – galutinio nuoseklumo tarp modulių visiškai pakanka.

Pažvelkime į konkretų pavyzdį. Turime orkestratoriaus koncepciją – konvejerį, kuris apdoroja „programos“ esybę. Jis paeiliui sukuria klientą, sąskaitą ir banko kortelę. Jei klientas ir paskyra sukurti sėkmingai, bet nepavyksta sukurti kortelės, programa nepereina į „sėkmingai“ būseną ir lieka „kortelė nesukurta“. Ateityje foninė veikla ją paims ir užbaigs. Sistema jau kurį laiką buvo nenuoseklioje būsenoje, tačiau iš esmės esame tuo patenkinti.

Susidarius situacijai, kai reikia nuosekliai išsaugoti dalį duomenų, greičiausiai eisime į paslaugos konsolidavimą, kad galėtume ją apdoroti vienu procesu.

Pažvelkime į mikropaslaugos paskirstymo pavyzdį. Kaip galite palyginti saugiai jį pristatyti į gamybą? Šiame pavyzdyje turime atskirą sistemos dalį – darbo užmokesčio apskaitos paslaugų modulį, kurio vieną iš kodų skyrių norėtume padaryti mikroservisą.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Visų pirma, perrašydami kodą sukuriame mikropaslaugą. Tobuliname kai kuriuos aspektus, kuriais nebuvome patenkinti. Įgyvendiname naujus verslo reikalavimus iš kliento. Prie vartotojo sąsajos ir užpakalinės sistemos sąsajos pridedame API šliuzą, kuris užtikrins skambučių peradresavimą.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Tada paleisime šią konfigūraciją, bet bandomojoje būsenoje. Dauguma mūsų vartotojų vis dar dirba su senais verslo procesais. Naujiems vartotojams kuriame naują monolitinės programos versiją, kurioje šio proceso nebėra. Iš esmės mes turime monolito ir mikroserviso derinį, veikiantį kaip pilotas.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Sėkmingai atlikus bandomąjį projektą, suprantame, kad nauja konfigūracija tikrai veikia, galime pašalinti iš lygties seną monolitą ir palikti naują konfigūraciją senojo sprendimo vietoje.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Iš viso mes naudojame beveik visus esamus monolito šaltinio kodo padalijimo metodus. Visi jie leidžia sumažinti programos dalių dydį ir išversti jas į naujas bibliotekas, kad būtų geresnis šaltinio kodas.

Darbas su duomenų baze


Duomenų bazė gali būti padalinta blogiau nei šaltinio kodas, nes joje yra ne tik dabartinė schema, bet ir sukaupti istoriniai duomenys.

Mūsų duomenų bazė, kaip ir daugelis kitų, turėjo dar vieną svarbų trūkumą – didžiulį dydį. Ši duomenų bazė buvo sukurta pagal sudėtingą monolito verslo logiką ir ryšius, sukauptus tarp įvairių ribotų kontekstų lentelių.

Mūsų atveju, prie visų bėdų (didelė duomenų bazė, daug jungčių, kartais neaiškios ribos tarp lentelių), iškilo problema, kuri pasitaiko daugelyje didelių projektų: bendros duomenų bazės šablono naudojimas. Duomenys buvo paimti iš lentelių per rodinį, per replikaciją ir siunčiami į kitas sistemas, kur to reikėjo. Dėl to negalėjome perkelti lentelių į atskirą schemą, nes jos buvo aktyviai naudojamos.

Tas pats suskirstymas į ribotus kontekstus kode padeda mums atsiskirti. Paprastai tai suteikia mums gana gerą supratimą apie tai, kaip suskirstome duomenis duomenų bazės lygiu. Suprantame, kurios lentelės priklauso vienam ribotam kontekstui, o kurios – kitam.

Mes naudojome du visuotinius duomenų bazių skaidymo būdus: esamų lentelių skaidymą ir skaidymą su apdorojimu.

Esamų lentelių atskyrimas yra geras būdas, jei duomenų struktūra yra gera, atitinka verslo reikalavimus ir visi ja patenkinti. Tokiu atveju esamas lenteles galime atskirti į atskirą schemą.

Skyriaus su apdorojimu reikia tada, kai verslo modelis labai pasikeitė, o lentelės mūsų visiškai nebetenkina.

Esamų lentelių padalijimas. Turime nuspręsti, ką atskirsime. Be šių žinių niekas neveiks, o čia mums padės ribotų kontekstų atskyrimas kode. Paprastai, jei suprantate kontekstų ribas šaltinio kode, tampa aišku, kurios lentelės turėtų būti įtrauktos į skyriaus sąrašą.

Įsivaizduokime, kad turime sprendimą, kuriame du monolitiniai moduliai sąveikauja su viena duomenų baze. Turime įsitikinti, kad tik vienas modulis sąveikauja su atskirtų lentelių skyriumi, o kitas pradeda sąveikauti su ja per API. Pirmiausia pakanka, kad per API būtų vykdomas tik įrašymas. Tai būtina sąlyga, kad galėtume kalbėti apie mikropaslaugų nepriklausomumą. Skaitymo ryšiai gali išlikti tol, kol nėra didelių problemų.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Kitas žingsnis yra tai, kad kodo skyrių, kuris veikia su atskirtomis lentelėmis, su apdorojimu arba be jo, galime atskirti į atskirą mikro paslaugą ir paleisti ją atskirame procese, konteineryje. Tai bus atskira paslauga, turinti ryšį su monolitine duomenų baze ir tomis lentelėmis, kurios tiesiogiai su ja nesusijusios. Monolitas vis dar sąveikauja skaitydamas su nuimama dalimi.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Vėliau šį ryšį pašalinsime, tai yra, monolitinės programos duomenų nuskaitymas iš atskirtų lentelių taip pat bus perkeltas į API.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Toliau iš bendrosios duomenų bazės parinksime lenteles, su kuriomis veikia tik naujoji mikropaslauga. Lenteles galime perkelti į atskirą schemą ar net į atskirą fizinę duomenų bazę. Vis dar yra skaitymo ryšys tarp mikroserviso ir monolitinės duomenų bazės, tačiau nėra ko jaudintis, tokioje konfigūracijoje ji gali gyvuoti gana ilgai.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Paskutinis žingsnis yra visiškai pašalinti visas jungtis. Tokiu atveju mums gali tekti perkelti duomenis iš pagrindinės duomenų bazės. Kartais norime pakartotinai panaudoti kai kuriuos duomenis ar katalogus, pakartotus iš išorinių sistemų keliose duomenų bazėse. Pas mus taip nutinka periodiškai.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Perdirbimo skyrius. Šis metodas yra labai panašus į pirmąjį, tik atvirkštine tvarka. Nedelsdami skiriame naują duomenų bazę ir naują mikropaslaugą, kuri sąveikauja su monolitu per API. Tačiau tuo pačiu metu išlieka duomenų bazės lentelių rinkinys, kurį ateityje norime ištrinti. Mums jo nebereikia, mes jį pakeitėme nauju modeliu.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Kad ši schema veiktų, tikriausiai reikės pereinamojo laikotarpio.

Tada galimi du būdai.

Pirmas: dubliuojame visus duomenis naujoje ir senoje duomenų bazėje. Tokiu atveju turime duomenų pertekliaus ir gali kilti sinchronizavimo problemų. Bet galime priimti du skirtingus klientus. Vienas veiks su nauja versija, kitas su senąja.

Antra: duomenis skirstome pagal kai kuriuos verslo kriterijus. Pavyzdžiui, sistemoje turėjome 5 produktus, kurie buvo saugomi senoje duomenų bazėje. Šeštąją įtraukiame į naują verslo užduotį į naują duomenų bazę. Tačiau mums reikės API šliuzo, kuris sinchronizuos šiuos duomenis ir parodys klientui, iš kur ir iš ko gauti.

Abu būdai veikia, rinkitės pagal situaciją.

Įsitikinus, kad viskas veikia, monolito dalis, kuri veikia su senomis duomenų bazės struktūromis, gali būti išjungta.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Paskutinis žingsnis yra pašalinti senas duomenų struktūras.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Apibendrinant galima teigti, kad turime problemų su duomenų baze: su ja dirbti sunku, palyginti su pirminiu kodu, sunkiau dalintis, bet tai galima ir reikia padaryti. Radome keletą būdų, kurie leidžia tai padaryti gana saugiai, tačiau vis tiek lengviau suklysti naudojant duomenis nei naudojant šaltinio kodą.

Darbas su šaltinio kodu


Taip atrodė šaltinio kodo schema, kai pradėjome analizuoti monolitinį projektą.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Jis gali būti grubiai padalintas į tris sluoksnius. Tai paleistų modulių, papildinių, paslaugų ir individualios veiklos sluoksnis. Tiesą sakant, tai buvo monolitinio sprendimo įėjimo taškai. Visi jie buvo sandariai uždaryti bendru sluoksniu. Tai turėjo verslo logiką, kuria paslaugos dalijosi, ir daug ryšių. Kiekviena paslauga ir papildinys naudojo iki 10 ar daugiau įprastų rinkinių, atsižvelgiant į jų dydį ir kūrėjų sąžinę.

Mums pasisekė, kad turėjome infrastruktūros bibliotekas, kurias buvo galima naudoti atskirai.

Kartais susiklostė situacija, kai kai kurie bendri objektai iš tikrųjų nepriklauso šiam sluoksniui, o buvo infrastruktūros bibliotekos. Tai buvo išspręsta pervadinant.

Didžiausią nerimą kėlė riboti kontekstai. Taip atsitiko, kad 3-4 kontekstai buvo sumaišyti viename bendrajame komplekte ir naudojami vienas kitam tose pačiose verslo funkcijose. Reikėjo suprasti, kur tai galima padalyti ir kokiomis ribomis, ir ką toliau daryti suskirstant šį padalijimą į šaltinio kodo rinkinius.

Suformulavome keletą kodo padalijimo proceso taisyklių.

Pirmasis: Mes nebenorėjome dalytis verslo logika tarp paslaugų, veiklos ir įskiepių. Norėjome, kad verslo logika būtų nepriklausoma mikropaslaugose. Kita vertus, mikropaslaugos idealiai laikomos paslaugomis, kurios egzistuoja visiškai nepriklausomai. Manau, kad toks požiūris yra šiek tiek švaistomas ir sunkiai įgyvendinamas, nes, pavyzdžiui, paslaugos C# kalboje bet kokiu atveju bus sujungtos standartine biblioteka. Mūsų sistema parašyta C#, kitų technologijų dar nenaudojome. Todėl nusprendėme, kad galime sau leisti naudoti bendrus techninius mazgus. Svarbiausia, kad juose nebūtų jokių verslo logikos fragmentų. Jei ant naudojamo ORM turite patogumo paketą, jo kopijavimas iš paslaugos į paslaugą yra labai brangus.

Mūsų komanda yra domenu pagrįsto dizaino gerbėja, todėl svogūnų architektūra mums puikiai tiko. Mūsų paslaugų pagrindas yra ne duomenų prieigos sluoksnis, o rinkinys su domeno logika, kuriame yra tik verslo logika ir neturintis ryšių su infrastruktūra. Tuo pačiu metu galime savarankiškai modifikuoti domeno rinkinį, kad išspręstume su sistemomis susijusias problemas.

Šiame etape susidūrėme su pirmąja rimta problema. Paslauga turėjo kreiptis į vieną domeno agregatą, norėjome, kad logika būtų nepriklausoma, o DRY principas mums čia labai trukdė. Kūrėjai norėjo pakartotinai panaudoti gretimų rinkinių klases, kad būtų išvengta dubliavimo, todėl domenai vėl buvo susieti. Išanalizavome rezultatus ir nusprendėme, kad galbūt problema taip pat slypi šaltinio kodo saugojimo įrenginio srityje. Turėjome didelę saugyklą, kurioje buvo visas šaltinio kodas. Viso projekto sprendimą buvo labai sunku surinkti vietinėje mašinoje. Todėl projekto dalims buvo sukurti atskiri smulkūs sprendimai ir niekas nedraudė prie jų pridėti kokį nors bendrą ar domeno komplektą ir pakartotinai panaudoti. Vienintelis įrankis, kuris neleido mums to padaryti, buvo kodo peržiūra. Bet kartais ir nepavykdavo.

Tada pradėjome pereiti prie modelio su atskiromis saugyklomis. Verslo logika nebepereina iš paslaugos į paslaugą, sritys tikrai tapo nepriklausomos. Apriboti kontekstai palaikomi aiškiau. Kaip pakartotinai panaudoti infrastruktūros bibliotekas? Atskyrėme juos į atskirą saugyklą, tada sudėjome į „Nuget“ paketus, kuriuos įdėjome į „Artifactory“. Atlikus bet kokius pakeitimus, surinkimas ir paskelbimas vyksta automatiškai.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Mūsų paslaugos pradėjo remtis vidaus infrastruktūros paketais taip pat, kaip ir išoriniais. Atsisiunčiame išorines bibliotekas iš Nuget. Norėdami dirbti su „Artifactory“, kur įdėjome šiuos paketus, naudojome dvi paketų tvarkykles. Mažose saugyklose taip pat naudojome „Nuget“. Saugyklose su keliomis paslaugomis naudojome paketą, kuris užtikrina didesnį modulių versijų nuoseklumą.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Taigi, dirbdami su šaltinio kodu, šiek tiek pakeitę architektūrą ir atskirdami saugyklas, savo paslaugas darome nepriklausomesnės.

Infrastruktūros problemos


Dauguma perėjimo prie mikropaslaugų trūkumų yra susiję su infrastruktūra. Jums reikės automatizuoto diegimo, jums reikės naujų bibliotekų, kad galėtumėte valdyti infrastruktūrą.

Rankinis montavimas aplinkoje

Iš pradžių aplinkoms skirtą sprendimą įdiegėme rankiniu būdu. Norėdami automatizuoti šį procesą, sukūrėme CI / CD konvejerį. Pasirinkome nuolatinį pristatymo procesą, nes nuolatinis diegimas verslo procesų požiūriu mums dar nepriimtinas. Todėl siuntimas eksploatacijai atliekamas naudojant mygtuką, o testavimui - automatiškai.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Mes naudojame Atlassian, Bitbucket šaltinio kodo saugojimui ir Bamboo statybai. Mums patinka kurti scenarijus „Cake“, nes tai tas pats, kas C#. Paruošti paketai ateina į „Artifactory“, o „Ansible“ automatiškai patenka į bandomuosius serverius, o po to juos galima iš karto išbandyti.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Atskiras kirtimas


Vienu metu viena iš monolito idėjų buvo numatyti bendrą medienos ruošą. Taip pat turėjome suprasti, ką daryti su atskirais diskuose esančiais žurnalais. Mūsų žurnalai įrašomi į tekstinius failus. Nusprendėme naudoti standartinį ELK kaminą. Į ELK nerašėme tiesiogiai per tiekėjus, bet nusprendėme, kad užbaigsime tekstinius žurnalus ir juose kaip identifikatorių įrašysime trace ID, pridėdami paslaugos pavadinimą, kad vėliau šiuos žurnalus būtų galima analizuoti.

Perėjimas nuo monolito prie mikropaslaugų: istorija ir praktika

Naudodami „Filebeat“ gauname galimybę rinkti savo žurnalus iš serverių, tada juos transformuoti, naudoti „Kibana“, kad sukurtume užklausas vartotojo sąsajoje ir pamatytume, kaip vyko skambutis tarp paslaugų. Trace ID labai padeda.

Su testavimu ir derinimu susijusios paslaugos


Iš pradžių visiškai nesupratome, kaip derinti kuriamas paslaugas. Su monolitu viskas buvo paprasta; paleidome jį vietine mašina. Iš pradžių tą patį bandė daryti su mikropaslaugomis, tačiau kartais norint visiškai paleisti vieną mikropaslaugą reikia paleisti kelias kitas, ir tai nepatogu. Supratome, kad turime pereiti prie modelio, kai vietiniame kompiuteryje paliekame tik tą paslaugą ar paslaugas, kurias norime derinti. Likusios paslaugos naudojamos iš serverių, kurie atitinka konfigūraciją su prod. Po derinimo, testavimo metu, kiekvienai užduočiai į testavimo serverį išduodamos tik pakeistos paslaugos. Taigi sprendimas yra išbandomas tokia forma, kokia jis pasirodys gamyboje ateityje.

Yra serverių, kuriuose veikia tik gamybinės paslaugų versijos. Šie serveriai reikalingi įvykus incidentams, norint patikrinti pristatymą prieš diegimą ir atliekant vidinius mokymus.

Pridėjome automatizuotą testavimo procesą naudodami populiarią Specflow biblioteką. Testai vykdomi automatiškai naudojant NUnit iš karto po įdiegimo iš Ansible. Jei užduočių aprėptis yra visiškai automatinė, nereikia atlikti rankinio testavimo. Nors kartais vis tiek reikalingas papildomas rankinis testavimas. Naudojame „Jira“ žymas, kad nustatytų, kuriuos testus reikia atlikti esant konkrečiai problemai.

Be to, išaugo apkrovos testavimo poreikis, anksčiau jie buvo atliekami tik retais atvejais. Mes naudojame JMeter testams vykdyti, InfluxDB jiems saugoti ir Grafana proceso grafikui kurti.

Ką mes pasiekėme?


Pirma, mes atsikratėme „išleidimo“ sąvokos. Dingo dviejų mėnesių siaubingi leidimai, kai šis kolosas buvo įdiegtas gamybinėje aplinkoje ir laikinai sutrikdė verslo procesus. Dabar paslaugas diegiame vidutiniškai kas 1,5 dienos, sugrupuodami jas, nes jos pradedamos veikti po patvirtinimo.

Mūsų sistemoje nėra mirtinų gedimų. Jei išleisime mikropaslaugą su klaida, tada su ja susieta funkcionalumas bus pažeistas, o visos kitos funkcijos nebus paveiktos. Tai labai pagerina vartotojo patirtį.

Mes galime valdyti diegimo modelį. Jei reikia, galite pasirinkti paslaugų grupes atskirai nuo likusio sprendimo.

Be to, šią problemą gerokai sumažinome dėl didelės patobulinimų eilės. Dabar turime atskiras produktų komandas, kurios su kai kuriomis paslaugomis dirba savarankiškai. Scrum procesas čia jau puikiai tinka. Konkreti komanda gali turėti atskirą produkto savininką, kuris jai priskiria užduotis.

Santrauka

  • Mikropaslaugos puikiai tinka sudėtingų sistemų skaidymui. Proceso metu pradedame suprasti, kas yra mūsų sistemoje, kokie yra riboti kontekstai, kur yra jų ribos. Tai leidžia teisingai paskirstyti patobulinimus tarp modulių ir išvengti kodo painiavos.
  • Mikropaslaugos teikia organizacinę naudą. Apie juos dažnai kalbama tik kaip apie architektūrą, tačiau bet kokia architektūra reikalinga verslo poreikiams spręsti, o ne pati. Todėl galime teigti, kad mikropaslaugos puikiai tinka sprendžiant problemas mažose komandose, atsižvelgiant į tai, kad Scrum šiuo metu yra labai populiarus.
  • Atskyrimas yra pasikartojantis procesas. Negalite paimti programos ir tiesiog suskirstyti ją į mikropaslaugas. Mažai tikėtina, kad gautas produktas bus funkcionalus. Skiriant mikropaslaugas pravartu perrašyti esamą palikimą, tai yra paversti jį mums patinkančiu ir verslo poreikius funkcionalumu bei greičiu labiau atitinkančiu kodu.

    Mažas įspėjimas: Perėjimo prie mikropaslaugų išlaidos yra gana nemažos. Vien infrastruktūros problemą išspręsti prireikė daug laiko. Taigi, jei turite nedidelę programą, kuriai nereikia specialaus mastelio, nebent turite daug klientų, kurie konkuruoja dėl jūsų komandos dėmesio ir laiko, mikropaslaugos gali būti ne tai, ko jums reikia šiandien. Tai gana brangu. Jei procesą pradėsite nuo mikropaslaugų, tada išlaidos iš pradžių bus didesnės nei tuo atveju, jei pradėsite tą patį projektą su monolito plėtra.

    PS Emocionalesnė istorija (ir tarsi tau asmeniškai) – pagal nuoroda.
    Čia yra visa ataskaitos versija.

Šaltinis: www.habr.com

Добавить комментарий