Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Në këtë artikull do të flas për atë se si projekti në të cilin punoj kaloi nga një monolit i madh në një grup mikroshërbimesh.

Projekti e ka nisur historinë e tij mjaft herët, në fillim të viteve 2000. Versionet e para ishin shkruar në Visual Basic 6. Me kalimin e kohës u bë e qartë se zhvillimi në këtë gjuhë do të ishte i vështirë për tu mbajtur, pasi IDE dhe vetë gjuha po zhvilloheshin ngadalë. Në fund të viteve 2000, u vendos të kalonim në një C# më premtues. Versioni i ri u shkrua paralelisht me përmirësimin e të vjetrës, duke u bërë gjithnjë e më shumë kod në .NET. Backend-i në C# në fillim ishte orientuar ndaj arkitekturës shërbimore, megjithatë gjatë zhvillimit u përdorën biblioteka të përbashkëta me logjikë, dhe shërbimet u nisën në një proces të vetëm. Kështu u krijua një aplikacion që ne e quajtëm 'monolit shërbimi'.

Një nga avantajet e pakta të këtij lidhjeje ishte mundësia e shërbimeve për të thirrur njëra-tjetrën përmes një API të jashtëm. Kishte premisat e qarta për kalimin në një arkitekturë më të saktë shërbimi, dhe në perspektivë edhe në mikroshërbime.

Kemi filluar punĂ«n tonĂ« pĂ«r dekompozimin rreth vitit 2015. Akoma nuk kemi arritur njĂ« gjendje ideale — kanĂ« mbetur pjesĂ« tĂ« projektit tĂ« madh, tĂ« cilat Ă«shtĂ« e vĂ«shtirĂ« t'i quash monolite, por as qĂ« i ngjajnĂ« mikrosherbimeve. MegjithatĂ«, progresi Ă«shtĂ« domethĂ«nĂ«s.
Për të do të flas në artikull.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Përmbajtja

Arkitektura dhe problemet e zgjidhjes ekzistuese


Arkitektura fillimisht dukej si mĂ« poshtĂ«: UI — aplikacion i veçantĂ«, pjesa monolite e shkruar nĂ« Visual Basic 6, aplikacioni nĂ« .NET pĂ«rbĂ«nte njĂ« grup shĂ«rbimesh tĂ« lidhura, duke punuar me njĂ« bazĂ« tĂ« dhĂ«nash tĂ« mjaftueshme tĂ« madhe.

Disavantazhet e zgjidhjes së mëparshme

Pika unike e dështimit
Ne kemi pasur një pikë të vetme dështimi: aplikacioni në .NET ekzekutohej në një proces të vetëm. Nëse ndonjë nga modulet kishte një dështim, aplikacioni i gjithë dështonte dhe duhej ta rinisnim. Duke qenë se ne automatizojmë një numër të madh procesesh për përdorues të ndryshëm, për shkak të një dështimi në njërin prej tyre, të gjithë nuk mund të punonin për një kohë. Dhe në rast të një gabimi programor, rezervimi nuk ndihmonte as.

Radhë për përmirësime
Ky disavantazh është më shumë organizativ. Në aplikacionin tonë ka shumë kërkues, dhe të gjithë duan që ta përmirësojnë sa më shpejt. Më parë, kryerja e kësaj paralelisht ishte e pamundur, dhe të gjithë kërkuesit qëndronin në radhë. Ky proces shkaktonte negativitet për biznesin, pasi atyre u duhej të dëshmonin se detyra e tyre kishte vlerë. Ekipi zhvillues shpenzonte kohë për të organizuar këtë radhë. Kjo merrte shumë kohë dhe energji, dhe si rezultat produkti nuk mund të ndryshonte aq shpejt sa do të donin.

Përdorim joadekuat i burimeve
Kur vendosni shërbimet në një proces të vetëm, ne gjithmonë e kopjojmë plotësisht konfigurimin nga serveri në server. Donim të vendosnim shërbimet më të ngarkuara veçmas, për të mos shpenzuar burime kotë dhe për të pasur një menaxhim më fleksibël të skemës sonë të shpërndarjes.

ËshtĂ« e vĂ«shtirĂ« tĂ« integrohen teknologjitĂ« moderne
Një problem i njohur për të gjithë zhvilluesit: ka dëshirë të integrohet teknologji moderne në projekt, por nuk ka mundësi. Në një zgjidhje të madhe monolitike, çdo azhurnim i bibliotekës ekzistuese, dhe të themi kalimi në të re, bëhet një detyrë mjaft e ndërlikuar. Duhet shumë kohë për t'i treguar liderit të ekipit se kjo do të sjellë më shumë përfitime se sa nervat e shpenzuara.

Vështirësitë në lëshimin e ndryshimeve
Kjo ishte problemi mĂ« serioz — ne lĂ«shonim versione çdo dy muaj.
Çdo version bĂ«hej njĂ« katastrofĂ« e vĂ«rtetĂ« pĂ«r bankĂ«n, pavarĂ«sisht testeve dhe pĂ«rpjekjeve tĂ« zhvilluesve. Biznesi e dinte se njĂ« pjesĂ« e funksionalitetit nuk do tĂ« punonte nĂ« fillim tĂ« javĂ«s. Dhe zhvilluesit e dinin se i priste njĂ« javĂ« e vĂ«shtirĂ« me incidente.
Dëshira për të ndryshuar situatën ishte e pranishme te të gjithë.

Pritet nga mikrosherbimet


Shpërndarja e komponentëve sipas gatishmërisë. Shpërndarja e komponentëve sipas gatishmërisë falë dekompozimit të zgjidhjes dhe ndarjes së proceseve të ndryshme.

Ekipet e vogla të produkteve. Kjo është e rëndësishme, sepse duke punuar me një ekip të madh mbi një monolit të vjetër, menaxhimi ishte i vështirë. Një ekip i tillë kishte nevojë të punonte sipas proceseve strikte, kurse ne donim më shumë krijimtari dhe pavarësi. Kjo mund të lejohej vetëm nga ekipe të vogla.

Izolimi i shërbimeve në procese të veçanta. Idealisht, do të doja të izoloja në kontejnerë, por një numër i madh shërbimesh të shkruara në .NET Framework funksionon vetëm në Windows. Tani po shfaqen shërbime në .NET Core, por ato janë ende të pakta.

Fleksibiliteti i shpërndarjes. Do të doja të kombinonte shërbimet ashtu siç na nevojitet, e jo siç e imponon kodi.

Përdorimi i teknologjive të reja. Kjo është interesante për çdo programues.

Problemet e kalimit


Sigurisht, nëse do të ishte e lehtë të prishje monolitin në mikroshërbime, nuk do të ishte nevojë të flisnim për këtë në konferenca dhe të shkruanin artikuj. Ka shumë pengesa në këtë proces, do t'i përshkruaj ato kryesoret që na penguan.

Problemi i parë tipike për shumicën e monoliteve: lidhshmëria e logjikës biznesore. Kur shkruajmë një monolit, dëshirojmë të ripërdorim klasat tona për të mos shkruar kod të tepërt. Por kur kalojmë në mikroshërbime, kjo bëhet një problem: tërë kodi është mjaft i lidhur dhe është e vështirë të ndajmë shërbimet.

Në momentin e fillimit të punës, në depo kishte më shumë se 500 projekte dhe më shumë se 700 mijë rreshta kodi. Kjo është një zgjidhje mjaft e madhe dhe problemi i dytë. Thjesht të merrni dhe ta ndani atë në mikroshërbime nuk dukej e mundur.

Problemi i tretĂ« — mungesa e infrastrukturĂ«s sĂ« nevojshme. NĂ« fakt, ne merreshim me kopjimin manual tĂ« kodit burimor nĂ« servera.

Si të kalosh nga monoliti në mikrosherbime


Shkëputja e mikroshërbimeve

SĂ« pari, ne patĂ«m qartazi tĂ« pĂ«rcaktonim se ndarja e mikroshĂ«rbimeve Ă«shtĂ« njĂ« proces iterativ. Na kĂ«rkohej gjithmonĂ« tĂ« vazhdonim tĂ« zhvillonim detyrat biznesore paralelisht. Si do ta realizonim atĂ« teknikisht — ishte problemi ynĂ«. Prandaj, ne u pĂ«rgatitĂ«m pĂ«r njĂ« proces iterativ. Ndryshe nuk do tĂ« ishte e mundur, nĂ«se keni njĂ« aplikacion tĂ« madh qĂ« nĂ« fillim nuk Ă«shtĂ« i gatshĂ«m pĂ«r t'u ripŰ§Ù† distuar.

Cilat mënyra përdorim për ndarjen e mikroshërbimeve?

Metoda e parĂ« — tĂ« nxjerrĂ«sh modul tĂ« pranishĂ«m si shĂ«rbime. NĂ« kĂ«tĂ« aspekt, na ka ndihmuar fati: kishim shĂ«rbime tĂ« dokumentuara qĂ« punonin me protokollin WCF. Ato ishin shpĂ«rndarĂ« nĂ« mbledhje tĂ« veçanta. Ne i transferuam ato pĂ«r veçmas, duke shtuar njĂ« modul tĂ« vogĂ«l startimi nĂ« secilĂ«n mbledhje. Ky modul ishte shkruar me bibliotĂškĂ«n e mrekullueshme Topshelf, e cila lejon fillimin e aplikacionit si shĂ«rbim dhe gjithashtu si konsolĂ«. Kjo Ă«shtĂ« e pĂ«rshtatshme pĂ«r debugim, pasi nuk kĂ«rkon projekte tĂ« tjera nĂ« zgjidhje.

Shërbimet ishin të lidhura sipas logjikës së biznesit, pasi përdornin mbledhje të përbashkëta dhe punonin me një bazë të dhënash të përbashkët. Ishin të vështira për t'u quajtur mikrosisteme në formën e tyre të pastër. Megjithatë, ne mund t'i ofronim këto shërbime veçmas, në procese të ndryshme. Kjo e lehtësoi ndikimin e tyre ndaj njëri-tjetrit, duke reduktuar problemin me zhvillimin paralel dhe pikën e vetme të dështimit.

Ndërtesa me hostin është thjesht një rresht kod në klasën Program. Ne e fshihnim punën me Topshelf në një klasë ndihmëse.

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

       }
    }
}

Mënyra e dytë e ndarjes së mikrosistemeve: t'i krijoni ato për të zgjidhur detyra të reja. Nëse gjatë kësaj kohe monolithi nuk rritet, kjo është e shkëlqyer, do të thotë se po lëvizim në drejtimin e duhur. Për të zgjidhur detyra të reja, ne përpiqemi të bëjmë shërbime të veçanta. Nëse ka qenë një mundësi, atëherë ne kemi krijuar shërbime më "kanonike", të cilat menaxhojnë plotësisht modelin e tyre të të dhënave, me një bazë të dhënash të ndarë.

Ne, si shumë të tjerë, filluam me shërbime autentikimi dhe autorizimi. Ato janë perfekte për këtë qëllim. Ato janë të pavarura, në përgjithësi, kanë një model të dhënash të veçuar. Ato vetë nuk ndërveprojnë me monolith-in, vetëm ai i drejtohet atyre për të zgjidhur ndonjë detyrë. Në këto shërbime mund të filloni kalimin në një arkitekturë të re, të shtroni infrastrukturën, të provoni disa qasje që lidhen me bibliotekat rrjet dhe të tjera. Në organizatën tonë nuk ka ekipe që nuk do të ishin në gjendje të bëjnë një shërbim autentikimi.

Mënyra e tretë e ndarjes së mikroshërbimeve, i cili është pak specifik për ne. Ky është izolimi i logjikës biznesore nga sloji UI. Aplikacioni ynë kryesor i UI është për desktop, si backend, është shkruar në C#. Zhvilluesit herë pas here gabonin dhe nxirrnin në UI pjesë logjike që duhej të ekzistonin në backend dhe të ripërdoroheshin.

Nëse shikojmë një shembull real nga kodi i pjesës UI, shihet se një pjesë e madhe e këtij zgjidhjeje përmban logjikë reale biznesi, e cila është e dobishme në procedura të tjera, jo vetëm për ndërtimin e formave UI.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Logjika reale e UI atje është vetëm dy rreshtat e fundit. Ne e transferuam atë në server për ta bërë të mundur ripërdorimin, duke e zvogëluar kështu UI-në dhe duke arritur një arkitekturë të saktë.

Mënyra e katërt, më e rëndësishme për ndarjen e mikroshërbjeve, i cili lejon zvogëlimin e monolitit, është nxjerrja e shërbimeve ekzistuese me rishikim. Kur ne nxjerrim modulat ekzistuese ashtu siç janë, rezultati nuk i pëlqen gjithmonë zhvilluesve, dhe procesi biznesor mund të jetë bërë i vjetruar që nga krijimi i funksionalitetit. Falë rishikimit të kodit, ne mund të mbështesim një proces të ri biznesor, sepse kërkesat e biznesit po ndryshojnë vazhdimisht. Ne mund të përmirësojmë kodin burimor, të heqim defektet e njohura, të krijojmë një model më cilësor të të dhënave. Mblidhen shumë përfitime.

Departamenti i shërbimeve me përpunim është ngushtë i lidhur me konceptin e kontekstit të kufizuar. Ky koncept vjen nga dizajni i orientuar drejt subjektit. Ai nënkupton një pjesë të modelit të domainit, ku të gjitha terma të një gjuhe janë të përcaktuara qartë. Le të shohim shembullin e konteksteve të sigurimeve dhe faturave. Kemi një aplikacion monolitik dhe është e nevojshme të punojmë me faturën në sigurime. Ne presim që zhvilluesi të gjejë në një ndërtim tjetër klasën ekzistuese "Faturë", të bëjë referencë ndaj saj nga klasa "Sigurimi", dhe do të kemi kod në funksion. Parimi DRY do të respektohet, detyra do të përfundohet më shpejt duke shfrytëzuar kodin ekzistues.

Më në fund, rezulton se kontekstet e faturave dhe sigurimeve janë të lidhura. Kur të shfaqen kërkesa të reja, kjo lidhje do të pengojë zhvillimin, duke rritur kompleksitetin e një logjike biznesi tashmë të komplikuar. Për të zgjidhur këtë problem, është e nevojshme të gjenden kufijtë midis konteksteve në kod dhe të eliminohen shkeljet e tyre. Për shembull, për kontekstin e sigurimeve, mund të mjaftojë numri 20-shifror i faturës së CB-së dhe data e hapjes së faturës.

Për të ndarë këto kontekste të kufizuara nga njëra-tjetra dhe për të filluar procesin e ndarjes së mikroshërbimeve nga një zgjidhje monolitike, ne përdorëm një qasje të tillë si krijimi i API-ve të jashtme brenda aplikacionit. Nëse dinim se ndonjë modul do të bëhej një mikroshërbim, ose do të transformohej në kuadër të procesit, ne menjëherë kryenim thirrje të logjikës që i përkiste një konteksti të kufizuar tjetër përmes thirrjeve të jashtme. Për shembull, përmes REST ose WCF.

Ne vendosĂ«m fort qĂ« nuk do tĂ« shmangim kodin qĂ« do tĂ« kĂ«rkonte realizimin e transaksioneve tĂ« shpĂ«rndara. NĂ« rastin tonĂ«, doli tĂ« ishte mjaft e lehtĂ« tĂ« respektohej kjo rregull. Ende nuk kemi hasur nĂ« situata tĂ« tilla kur transaksionet e forta tĂ« shpĂ«rndara ishin vĂ«rtet tĂ« nevojshme — Ă«shtĂ« mjaft e mjaftueshme konsistenca pĂ«rfundimtare midis moduleve.

Le tĂ« shqyrtojmĂ« njĂ« shembull konkret. Kemi konceptin e orkestruesit — njĂ« linjĂ« prodhimi qĂ« proceson entitetin "aplikimi". Ai renditshĂ«m krijon klientin, llogarinĂ« dhe kartĂ«n bankare. NĂ«se klienti dhe llogaria janĂ« krijuar me sukses, por krijimi i kartĂ«s ka dĂ«shtuar, aplikimi nuk kalon nĂ« statusin "i suksesshĂ«m" dhe mbetet nĂ« statusin "nuk Ă«shtĂ« krijuar karta". NĂ« tĂ« ardhmen, aktiviteti pasiv do ta kapĂ« atĂ« dhe do ta pĂ«rfundojĂ«. Sistemi ndodhet pĂ«r njĂ« kohĂ« nĂ« njĂ« gjendje tĂ« mosshkallĂ«zimit, por kjo na kĂ«naq pĂ«rgjithĂ«sisht.

Në rast se ndodh ndonjëherë një situatë ku do të jetë e nevojshme të ruajmë një pjesë të të dhënave në mënyrë të qëndrueshme, ne, me siguri, do të shkojmë drejt konsolidimit të shërbimit për ta procesuar këtë në një proces.

Le tĂ« shqyrtojmĂ« shembullin e ndarjes sĂ« njĂ« mikroshĂ«rbimi. Si mund ta çojmĂ« atĂ« nĂ« prodhim nĂ« mĂ«nyrĂ« relativisht tĂ« sigurt? NĂ« kĂ«tĂ« shembull, kemi njĂ« pjesĂ« tĂ« veçantĂ« tĂ« sistemit — modulin e shĂ«rbimit tĂ« pagave, njĂ« nga pjesĂ«t e kodit tĂ« cilin do tĂ« dĂ«shironim ta bĂ«jmĂ« mikroshĂ«rbim.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

E para së parës, ne krijojmë një mikroshërbim duke e riparë kodin. Përmirësojmë disa aspekte që nuk na kënaqnin. Zhvillojmë kërkesat e reja të biznesit nga klienti. Shtojmë në lidhjen midis UI dhe backend-it një API Gateway, i cili do të sigurojë kalimin e thirrjeve.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Më pas ne lëshojmë këtë konfigurim në përdorim, por në gjendje piloti. Shumica e përdoruesve tanë ende funksionojnë me proceset e vjetra të biznesit. Për përdoruesit e rinj, ne po zhvillojmë një version të ri të aplikacionit monolit, i cili nuk përmban më këtë proces. Në thelb, kemi një lidhje midis monolit dhe mikroshërbimit në formën e një piloti.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Nëse piloti është i suksesshëm, ne kuptojmë se konfigurimi i ri është në të vërtetë funksional, mund të heqim nga ekuacioni monolit e vjetër dhe ta lëmë konfigurimin e ri në vendin e zgjidhjes së vjetër.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Pra, ne përdorim praktikisht të gjitha metodat ekzistuese për ndarjen e kodit burimor të monolit. Të gjitha këto na lejojnë të zvogëlojmë përmasat e pjesëve të aplikacionit dhe t'i transferojmë ato në biblioteka të reja, duke krijuar një kod burimor më cilësor.

Puna me DB


BD-ja ndahet më keq se kodi burimor, pasi përmban jo vetëm skemën aktuale, por edhe të dhëna historike të akumuluara.

BD-ja jonĂ«, ashtu si shumĂ« tĂ« tjera, kishte njĂ« tjetĂ«r mangĂ«si tĂ« rĂ«ndĂ«sishme — madhĂ«sinĂ« e saj tĂ« madhe. Kjo BD u projektua sipas logjikĂ«s sĂ« ndĂ«rlikuar tĂ« biznesit tĂ« monolit, dhe midis tabelave tĂ« konteksteve tĂ« ndryshme tĂ« kufizuara u akumuluan lidhje.

Në rastin tonë, përveç të gjitha të këqijave (BD e madhe, shumë lidhje, ndonjëherë kufij të paqartë midis tabelave) u shfaq një problem që haset në shumë projekte të mëdha: përdorimi i modelit të bazës së të dhënave të ndara. Të dhënat merreshin nga tabelat përmes view-t, përmes replikimit dhe dërgoheshin në sisteme të tjera ku nevojitej kjo replikim. Si rezultat, ne nuk mundëm të nxirrnim tabelat në një skemë të veçantë, sepse ato ishin aktivisht të përdorura.

Në ndarjen na ndihmon ai ndarje në kontekste të kufizuara në kod. Ajo, si rregull, na jep një ide të mirë se si i ndajmë të dhënat në nivelin e bazës së të dhënave. Ne e kuptojmë se cilat tabela i përkasin një konteksti të kufizuar, dhe cilat një tjetër.

Ne we kemi aplikuar dy qasje globale për ndarjen e bazës së të dhënave: ndarjen e tabelave ekzistuese dhe ndarjen me riformatim.

Ndarja e tabelave ekzistuese është një metodë që është mirë të aplikohet kur struktura e të dhënave është e mirë, përmbush kërkesat e biznesit dhe është e pranueshme për të gjithë. Në këtë rast, ne mund të veçojmë tabelat ekzistuese në një skemë të veçantë.

Ndarja me riformatim është e nevojshme kur modeli i biznesit ka ndryshuar ndjeshëm dhe tabelat nuk na plotësojnë më.

Ndarja e tabelave ekzistuese. Na nevojitet të përcaktojmë se çfarë do të ndarim. Pa këtë njohuri, nuk do të arrijmë dot asgjë, dhe këtu na ndihmon ndarja e konteksteve të kufizuara në kod. Në përgjithësi, nëse arrijmë të kuptojmë kufijtë e konteksteve në kodin burimor, bëhet e qartë se cilat tabela duhet të përfshihen në listën për ndarje.

Le të supozojmë se kemi një zgjidhje ku dy module të një monoliti ndërveprojnë me një bazë të dhënash. Ne na nevojitet të bëjmë që vetëm një modul të ndërveprojë me pjesën e tabelave të ndara, ndërsa tjetri të fillojë të lidhet me të përmes API-së. Në fillim, mjafton që përmes API-së të kryhet vetëm shkruajta. Kjo është një kusht i nevojshëm për të folur për pavarësinë e mikroshërbimeve. Lidhjet për lexim mund të mbeten, derisa kjo mos të jetë një problem i madh.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Hapi tjetër është se ne tashmë mund ta izolojmë pjesën e kodit që punon me tabelat e ndara, duke e ripërpunuar apo jo, në një mikroshërbim të veçantë dhe ta ekzekutojmë në një proces të veçantë, konteiner. Kjo do të jetë një shërbim i veçantë me një lidhje me bazën e të dhënave të monolit dhe ato tabela që nuk i përkasin direkt atij. Monoliti akoma interkonkordon për lexim me pjesën e ndarë.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Më vonë, ne do ta heqim këtë lidhje, pra, leximi i të dhënave të aplikacionit monolit nga tabelat e ndara gjithashtu do të kalojë në API.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Më vonë, ne do të ndajmë nga baza e dhënash të përgjithshme tabelat me të cilat punon vetëm mikroservisi i ri. Mund të nxjerrim tabelat në një skemë të veçantë ose madje në një bazë të veçantë fizike të të dhënave. Kemi mbetur lidhjen për lexim midis mikroservisit dhe bazës së të dhënave të monolit, por në këtë nuk ka asgjë të keqe; në këtë konfigurim ai mund të jetojë mjaft gjatë.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Hapi i fundit është të heqim plotësisht të gjitha lidhjet. Në këtë rast, ndoshta do të na nevojitet migrimi i të dhënave nga baza kryesore. Ndonjëherë, ne do të duam të ri-përdorim në disa baza disa të dhëna që replikohen nga sisteme të jashtme ose referenca. Kjo na ndodh Periodikisht.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Shkëputje me ripërpunim. Ky metodë është shumë e ngjashme me të parin, vetëm se shkon në rendin e kundërt. Ne menjëherë kemi një bazë të dhënash të re dhe një mikroservis të ri, i cili bashkëvepron me monolitin përmes API-së. Por, njëkohësisht mbetet një grup tabelash në bazën e të dhënave që duam t'i heqim në të ardhmen. Ato nuk na nevojiten më, në modelin e ri i kemi zëvendësuar.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Që kjo skemë të funksionojë, me siguri do të na nevojitet një periudhë kaluese.

Më pas ka dy qasje të mundshme.

I parë: ne e kopjojmë të gjitha të dhënat në datat e reja dhe të vjetra. Në këtë rast, kemi tepërsi të dhënash, mund të shfaqen probleme me sinkronizimin. Por, mund të marrim dy klientë të ndryshëm. Njëri do të punojë me versionin e ri, tjetri me versionin e vjetër.

E dyta: ndajmĂ« tĂ« dhĂ«nat sipas ndonjĂ« kriteri biznesi. PĂ«r shembull, nĂ« sistemin tonĂ« kemi pasur 5 produkte qĂ« ruhen nĂ« bazĂ«n e tĂ« dhĂ«nave tĂ« vjetĂ«r. Produkti i gjashtĂ«, nĂ« kuadĂ«r tĂ« njĂ« detyre tĂ« re biznesi, e vendosim nĂ« BDB-nĂ« e re. Por do na nevojitet njĂ« API Gateway qĂ« do tĂ« sinkronizojĂ« kĂ«to tĂ« dhĂ«na dhe do t’i tregojĂ« klientit se nga ku dhe çfarĂ« duhet tĂ« marrĂ«.

Të dyja qasjet funksionojnë, zgjidhni në varësi të situatës.

Pas sigurimit që gjithçka funksionon, një pjesë e monolitit që punon me strukturat e vjetra të BDB-së mund të çaktivizohet.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Hapi i fundit do të jetë të fshijmë strukturat e vjetra të të dhënave.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Duke përmbledhur, mund të themi se kemi probleme me BDB-në: është e vështirë të punosh me të krahasuar me kodin burimor, është më e vështirë të ndahet, por mund ta bësh dhe duhet ta bësh. Ne kemi gjetur disa mënyra që e lejojnë këtë të bëhet mjaft në mënyrë të sigurt; përsëri, të bësh gabime me të dhënat është më e lehtë se sa me kodin burimor.

Puna me kodin burimor


Ky ishte pamja e skemës së kodit burimor kur filluam analizimin e projektit monolit.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Ajo mund tĂ« ndahet nĂ« tri shtresa. Kjo Ă«shtĂ« shtresa e modules, plugins, shĂ«rbimeve dhe aktiviteteve tĂ« veçanta. NĂ« thelb, kĂ«to ishin pikĂ« hyrjeje brenda zgjidhjes monolitike. TĂ« gjitha ishin tĂ« lidhura ngusht me shtresĂ«n Common. Aty ishte logjika e biznesit qĂ« pĂ«rdorej sĂ« bashku nga shĂ«rbimet dhe shumĂ« lidhje. Çdo shĂ«rbim dhe plugin pĂ«rdorte deri nĂ« 10 mĂ« shumĂ« e mbledhjeve common, varĂ«sisht nga madhĂ«sia e tyre dhe ndershmĂ«ria e zhvilluesve.

Na kishte bërë fat, kishim biblioteka infrastrukturore që mund të përdoren veçmas.

Ndonjëherë ndodhte që disa objekte Common në të vërtetë nuk i përkisnin kësaj shtrese, por ishin biblioteka infrastrukturore. Kjo zgjidhej me riemërim.

Më shumë shqetësim shkaktuan kontekstet e kufizuara. Ndonjëherë, 3-4 kontekste përziheshin në një ndërtim të zakonshëm dhe përdornin njëri-tjetrin brenda funksioneve të biznesit. Duhej të kuptonim se ku mund të ndaheshin dhe cilat ishin kufijtë, dhe çfarë duhet bërë më pas me grupimin e kësaj ndarje në ndërtimet e kodit burimor.

Kemi formuluar disa rregulla për procesin e ndarjes së kodit.

E para: ne donim më të ndajmë logjikën e biznesit midis shërbimeve, aktiviteteve dhe pluginëve. Donim që logjika e biznesit të ishte e pavarur brenda mikroshërbimeve. Nga ana tjetër, mikroshërbimet, në idealin e tyre, përceptohen si shërbime që ekzistojnë krejtësisht të pavarura. Mendoj se ky qasje është disi e tepruar dhe e vështirë për t'u arritur, sepse, për shembull, shërbimet në C# do të jenë gjithsesi të lidhura me bibliotekën standarde. Sistemi ynë është shkruar në C#, teknologji të tjera akoma nuk kemi qenë të detyruar të përdorim. Prandaj, vendosëm që mund të lejojmë vetes të përdorim ndërtimet teknike të zakonshme. E rëndësishme është që në to të mos ketë asnjë fragment të logjikes së biznesit. Nëse keni një mbështetje të përshtatshme mbi ORM-në që përdorni, atëherë kopjimi i saj nga një shërbim në një tjetër është shumë i shtrenjtë.

Ekipi ynë është fans i dizajnimit të orientuar nga objekti, prandaj "arkitektura me qepë" na i përshtatet në mënyrë të shkëlqyer. Baza e shërbimeve tona është një grumbullim me logjikën domain, e cila përmban vetëm logjikën biznesore dhe është e lirë nga lidhjet me infrastrukturën. Kështu, ne mund të përmirësojmë në mënyrë të pavarur grumbullimin domain për të zgjidhur problemet që kanë të bëjnë me kornizat.

Në këtë fazë, ne u përballëm me problemin e parë serioz. Shërbimi duhej të referohej në një ndërtim të vetëm të domainit, logjika që do të dëshironim ta bënim të pavarur, dhe prinsipin DRY na pengonte shumë. Zhvilluesit dëshironin të ridashuronin klasat nga ndërtimet përreth për të shmangur dyfishimin dhe si rezultat, domenet filluan përsëri të lidheshin me njëra-tjetrën. Ne analizuam rezultatet dhe vendosëm se ndoshta problemi ishte gjithashtu në fushën e strukturës së depozitës së kodit burimor. Ne kishim një depo të madhe, ku ishin të gjitha kodet burimore. Zgjidhja për të gjithë projektin ishte shumë e vështirë për t'u ndërtuar në makinat lokale. Prandaj, për pjesët e projektit krijoheshin zgjidhje të vogla ndarazi, dhe askush nuk e ndalonte të shtonte ndonjë ndërtim të zakonshëm ose të domainit dhe të ri-shfrytëzonte. Instrumenti i vetëm që nuk na lejonte ta bëjmë këtë ishte rishikimi i kodit. Por ndonjëherë, edhe ai dështonte.

AtĂ«herĂ« ne filluam tĂ« kalonim nĂ« njĂ« model me depo tĂ« veçanta. Logjika e biznesit nuk pĂ«rfundoi mĂ« nga njĂ« shĂ«rbim nĂ« tjetrin, domenet vĂ«rtet u bĂ«nĂ« tĂ« pavarura. Kontekstet e kufizuara mbĂ«shteten mĂ« qartĂ«. Si e rishfrytĂ«zojmĂ« bibliotekat infrastrukturore? Ne i ndamĂ« ato nĂ« njĂ« depo tĂ« veçantĂ«, atĂ«herĂ« i vendosĂ«m nĂ« paketat Nuget, tĂ« cilat i vendosĂ«m nĂ« Artifactory. Çdo herĂ« qĂ« ndodhin ndryshime, ndĂ«rtimi dhe publikimi ndodhin automatikisht.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Shërbimet tona filluan të referohen në paketat e brendshme infrastrukturore në të njëjtën mënyrë siç bëjmë me bibliotekat e jashtme. Ne shkarkojmë bibliotekat e jashtme nga Nuget. Për të punuar me Artifactory, në të cilin vendosnim këto paketa, përdorëm dy menaxherë paketash. Në depo të vogla ne gjithashtu përdorëm Nuget. Në depo me disa shërbime ne përdorëm Paket, i cili siguron më shumë konsistencë të versioneve midis moduleve.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Kështu, duke punuar me kodin burimor, duke ndryshuar pak arkitekturën dhe duke ndarë depo, ne e bëjmë shërbimet tona më të pavarura.

Problemet e infrastrukturës


Shumica e pengesave gjatë kalimit në mikroshërbime lidhet me infrastrukturën. Ju nevojitet një shpërndarje automatike, do të nevojiten biblioteka të reja për të punuar me infrastrukturën.

Instalimi manual në mjedise

Fillimisht, ne vendosnim zgjidhjet në mjedise manualisht. Për të automatizuar këtë proces, kemi krijuar një pipeline CI/CD. Zgjedhim procesin e shpërndarjes së vazhdueshme, sepse shpërndarja e vazhdueshme për ne aktualisht nuk është e pranueshme për sa i përket proceseve të biznesit. Prandaj, dërgimi në prodhim bëhet me një buton, ndërsa testimi është automatik.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Ne përdorim Atlassian, Bitbucket për ruajtjen e kodit burimor dhe Bamboo për ndërtimin. Na pëlqen të shkruajmë skriptet e ndërtimit në Cake, sepse është e njëjta si C#. Paketat e gatshme vijnë në Artifactory, dhe Ansible automatikisht shkon në serverat e testimit, pas së cilës ato mund të testohet menjëherë.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Regjistrimi i ndarë


Në kohën e saj, një nga idetë e monolitit ishte sigurimi i regjistrimit të përbashkët. Na nevojitej gjithashtu të kuptonim se çfarë duhet të bëjmë me regjistrimet e veçanta që ishin të ruajtura në disqe. Regjistrimet tona shkruhen në skedarë teksti. Ne vendosëm të përdorim grumbullin standard ELK. Nuk shkruam direkt në ELK përmes ofruesve, por vendosëm që t'i përmirësojmë regjistrimet e tekstit dhe t'i regjistrojmë ato me ID-në e gjurmës në formën e një identifikuesi, duke shtuar emrin e shërbimit, për të mundësuar analizimin e këtyre regjistrimeve më vonë.

Kalimi nga monoliti në mikroshërbime: historia dhe praktika

Me ndihmën e Filebeat, ne kemi mundësinë të mbledhim regjistrimet tona nga servera, pastaj t'i transformojmë ato, përmes Kibana të ndërtojmë kërkesa në UI dhe të shohim si po shkonte thirrja midis shërbimeve. ID-ja e gjurmës ndihmon shumë në këtë.

Testimi dhe debugimi i shërbimeve të lidhura


Fillimisht nuk e kuptonim plotësisht se si duhet t'i rregullonim shërbimet që po zhvillonim. Me monolitin gjithçka ishte e thjeshtë, e nisim në kompjuterin lokal. Fillimisht përpiqeshim të bënim të njëjtën gjë me mikroshërbimet, por ndonjëherë për të nisur një mikroshërbim të plotë, duhej të niste edhe disa të tjerë, gjë që ishte e pamundur. E kuptuam se ishte e nevojshme të kalonim në një model ku lëmë në kompjuterin lokal vetëm shërbimin ose shërbimet që duam të rregullojmë. Shërbimet e tjera përdoren nga serverë që përputhen me konfigurimin e prodhimit. Pas rregullimit, gjatë testimit, për çdo detyrë në serverin e testimit mbërrijnë vetëm shërbimet e ndryshuara. Kështu, testohet zgjidhja në atë formë në të cilën do të jetë në të ardhmen në prodhim.

Ka serverë ku qëndrojnë vetëm versionet e prodhimit të shërbimeve. Këta serverë janë të nevojshëm për rastet e incidenteve, për kontrollimin e dorëzimit përpara distribuimit dhe për trajnime të brendshme.

Ne kemi shtuar një proces automatizimi të testimit duke përdorur bibliotekën popullore Specflow. Testet ekzekutohen automatikisht me NUnit menjëherë pas implementimit nga Ansible. Nëse mbulimi i detyrës është plotësisht automatizuar, nuk është e nevojshme testimi manual. Megjithatë, ndonjëherë kërkohet akoma testim manual shtesë. Për të përcaktuar se cilat teste duhet të ekzekutohen për një detyrë të caktuar, ne përdorim etiketa në Jira.

Shtuar gjithashtu nevoja pĂ«r testim ngarkese, qĂ« mĂ« parĂ« kryhej vetĂ«m nĂ« raste tĂ« rralla. PĂ«r tĂ« ekzekutuar testet, ne pĂ«rdorim JMeter, pĂ«r ruajtjen e tyre — InfluxDB, dhe pĂ«r ndĂ«rtimin e grafikĂ«ve tĂ« procesit — Grafana.

ÇfarĂ« arritĂ«m?


Së pari, ne u shkëputëm nga koncepti i "lëshimit". U zhdukën lëshimet monstruoze dy-mujore, kur kjo makinë implementohej në ambientin production dhe dëmtonte përkohësisht proceset biznesore. Tani ne implementojmë shërbimet mesatarisht çdo 1.5 ditë, duke i grupuar ato, sepse ato dalin në prodhim pas miratimit.

Në sistemin tonë nuk ka defekte fatale. Nëse ne publikojmë një mikroshërbim me gabime, funksionaliteti i lidhur do të dështojë, ndërsa funksionalitetet e tjera nuk do të preken. Kjo e përmirësone ndjeshëm përvojën e përdoruesit.

Ne mund të menaxhojmë modelin e shpërndarjes. Mund të veçojmë grupe shërbimesh nga zgjidhja e përgjithshme, nëse është e nevojshme.

Për më tepër, ne kemi reduktuar ndjeshëm problemin me radhët e gjata të ripunimeve. Kemi formuar ekipe të veçanta produktesh që punojnë me pjesën e shërbimeve në mënyrë të pavarur. Procesi i Scrum-it i përshtatet shumë mirë këtu. Një ekipi të caktuar mund t'i ketë pronarin e vet të produktit, i cili i përcakton detyrat.

Curriculum Vitae

  • MikroshĂ«rbimet janĂ« shumĂ« tĂ« pĂ«rshtatshme pĂ«r dekompozimin e sistemeve komplekse. GjatĂ« procesit, fillojmĂ« tĂ« kuptojmĂ« se çfarĂ« ndodhet nĂ« sistemin tonĂ«, cilat janĂ« kontekstet e kufizuara dhe ku kalojnĂ« kufijtĂ« e tyre. Kjo lejon ndarjen e duhur tĂ« ripunimeve sipas moduleve dhe parandalon konfuzionin nĂ« kod.
  • Mikroserviset ofrojnĂ« pĂ«rparĂ«si organizative. Ato shpesh fliten vetĂ«m si njĂ« arkitekturĂ«, por çdo arkitekturĂ« i nevojitet zgjidhjes sĂ« kĂ«rkesave tĂ« biznesit dhe jo si njĂ« qĂ«llim vetĂ« nĂ« vetvete. Prandaj, mund tĂ« themi se mikroserviset janĂ« tĂ« pĂ«rshtatshme pĂ«r zgjidhjen e detyrave nga ekipe tĂ« vogla, duke marrĂ« parasysh qĂ« tani Ă«shtĂ« shumĂ« e njohur metodologia Scrum.
  • ShkĂ«putja Ă«shtĂ« njĂ« proces iterativ. Nuk Ă«shtĂ« e mundur tĂ« merrni njĂ« aplikacion dhe thjesht ta ndani atĂ« nĂ« mikroserviset. Produkti i rezultuar me siguri nuk do tĂ« jetĂ« funksional. GjatĂ« ndarjes sĂ« mikroserviseve, Ă«shtĂ« e leverdisshme tĂ« riprogramoni kodin ekzistues legacy, duke e kthyer atĂ« nĂ« kodin qĂ« na pĂ«lqen dhe qĂ« pĂ«rmbush mĂ« mirĂ« kĂ«rkesat e biznesit nĂ« lidhje me funksionalitetin dhe shpejtĂ«sinĂ«.

    Një përkushtim i vogël: shpenzimet për kalimin në mikroshërbime janë mjaft të konsiderueshme. Vetëm zgjidhja e problemeve të infrastrukturës mori shumë kohë. Prandaj, nëse keni një aplikacion të vogël që nuk kërkon shkallëzim specifik, nëse nuk ka shumë klientë që luftojnë për vëmendjen dhe kohën e ekipit tuaj, ndoshta mikroshërbimet nuk janë ajo që ju nevojitet sot. Ato janë mjaft të shtrenjta. Nëse e filloni procesin me mikroshërbime, shpenzimet në fillim do të jenë më të larta se sa nëse e nisni të njëjtin projekt me zhvillimin e një monoliti.

    P.S. NjĂ« tregim mĂ« emocional (dhe sikur t’ju ndodhte ju) – pĂ«r lidhjes.
    Këtu është versioni i plotë i raportit.

Burimi: habr.com

Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Bleni hostim tĂ« besueshĂ«m pĂ«r faqe me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster