Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Tässä artikkelissa puhun siitä, kuinka käsittelemäni projekti muuttui suuresta monoliitista mikropalveluiden joukoksi.

Projekti aloitti historiansa melko kauan sitten, vuoden 2000 alussa. Ensimmäiset versiot kirjoitettiin Visual Basic 6:lla. Ajan myötä kävi selväksi, että tällä kielellä kehitystä on tulevaisuudessa vaikea tukea, koska IDE ja itse kieli ovat huonosti kehittyneitä. 2000-luvun lopulla päätettiin siirtyä lupaavampaan C#:aan. Uusi versio kirjoitettiin rinnakkain vanhan version kanssa, vähitellen kirjoitettiin yhä enemmän koodia .NET:iin. C#:n taustaohjelma keskittyi alun perin palveluarkkitehtuuriin, mutta kehityksen aikana käytettiin yhteisiä logiikkakirjastoja ja palvelut lanseerattiin yhdessä prosessissa. Tuloksena oli sovellus, jota kutsuimme "palvelumonoliitiksi".

Yksi tämän yhdistelmän harvoista eduista oli palvelujen kyky soittaa toisilleen ulkoisen API:n kautta. Siirtymiselle oikeampaan palveluun ja tulevaisuudessa mikropalveluarkkitehtuuriin oli selkeät edellytykset.

Aloitimme hajotustyömme vuoden 2015 tienoilla. Ihanteelliseen tilaan emme ole vielä päässeet - suuressa projektissa on vielä osia, joita tuskin voi kutsua monoliitteiksi, mutta ne eivät myöskään näytä mikropalveluilta. Siitä huolimatta edistys on merkittävää.
Puhun siitä artikkelissa.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Pitoisuus

Olemassa olevan ratkaisun arkkitehtuuri ja ongelmat


Aluksi arkkitehtuuri näytti tältä: käyttöliittymä on erillinen sovellus, monoliittinen osa on kirjoitettu Visual Basic 6:lla, .NET-sovellus on joukko toisiinsa liittyviä palveluita, jotka toimivat melko suuren tietokannan kanssa.

Edellisen ratkaisun haitat

Yksi epäonnistumispiste
Meillä oli yksi vikakohta: .NET-sovellus suoritettiin yhdessä prosessissa. Jos jokin moduuli epäonnistui, koko sovellus epäonnistui ja se oli käynnistettävä uudelleen. Koska automatisoimme suuren määrän prosesseja eri käyttäjille, kaikki eivät voineet työskennellä jonkin aikaa johtuen yhden heistä. Ja ohjelmistovirheen tapauksessa edes varmuuskopiointi ei auttanut.

Parannusten jono
Tämä epäkohta on melko organisatorinen. Sovelluksellamme on monia asiakkaita, ja he kaikki haluavat parantaa sitä mahdollisimman pian. Aikaisemmin tätä ei ollut mahdollista tehdä rinnakkain, ja kaikki asiakkaat seisoivat jonossa. Tämä prosessi oli negatiivinen yrityksille, koska heidän oli todistettava, että heidän tehtävänsä oli arvokas. Ja kehitystiimi käytti aikaa tämän jonon järjestämiseen. Tämä vei paljon aikaa ja vaivaa, eikä tuote lopulta voinut muuttua niin nopeasti kuin he olisivat halunneet.

Resurssien epäoptimaalinen käyttö
Kun isännöimme palveluita yhdessä prosessissa, kopioimme kokoonpanon aina kokonaan palvelimelta palvelimelle. Halusimme sijoittaa eniten kuormitetut palvelut erilleen, jotta resursseja ei tuhlata ja jotta käyttöönottosuunnitelmamme olisi joustavampi.

Vaikea ottaa käyttöön nykyaikaisia ​​tekniikoita
Kaikille kehittäjille tuttu ongelma: nykyaikaista teknologiaa halutaan ottaa käyttöön projektissa, mutta siihen ei ole mahdollisuutta. Suurella monoliittisella ratkaisulla mikä tahansa nykyisen kirjaston päivitys, puhumattakaan siirtymisestä uuteen, muuttuu melko ei-triviaaliksi tehtäväksi. Kestää kauan todistaa joukkueen johtajalle, että tämä tuo enemmän bonuksia kuin hukkaan heitettyjä hermoja.

Vaikeus tehdä muutoksia
Tämä oli vakavin ongelma - julkaisimme julkaisuja kahden kuukauden välein.
Jokainen julkaisu muuttui todelliseksi katastrofiksi pankille huolimatta kehittäjien testauksesta ja ponnisteluista. Yritys ymmärsi, että viikon alussa osa sen toiminnoista ei toiminut. Ja kehittäjät ymmärsivät, että viikko vakavia tapauksia odotti heitä.
Kaikilla oli halu muuttaa tilannetta.

Odotukset mikropalveluilta


Komponenttien ongelma, kun se on valmis. Komponenttien toimitus valmiina hajottamalla liuos ja erottamalla eri prosessit.

Pienet tuoteryhmät. Tämä on tärkeää, koska vanhan monoliitin parissa työskentelevää suurta tiimiä oli vaikea hallita. Tällainen tiimi pakotettiin työskentelemään tiukan prosessin mukaisesti, mutta he halusivat lisää luovuutta ja itsenäisyyttä. Vain pienillä joukkueilla oli varaa tähän.

Palvelujen eristäminen erillisissä prosesseissa. Ihannetapauksessa halusin eristää sen säilöihin, mutta monet .NET Frameworkiin kirjoitetut palvelut toimivat vain Windowsissa. NET Core -pohjaiset palvelut ovat nyt ilmestymässä, mutta niitä on vielä vähän.

Käyttöönoton joustavuus. Haluamme yhdistää palvelut haluamallamme tavalla, ei niin kuin koodi pakottaa.

Uusien teknologioiden käyttö. Tämä on kiinnostavaa jokaiselle ohjelmoijalle.

Siirtymäongelmat


Tietysti, jos monoliitti olisi helppo murtaa mikropalveluiksi, ei siitä tarvitsisi puhua konferensseissa ja kirjoittaa artikkeleita. Tässä prosessissa on monia sudenkuoppia; kuvailen tärkeimmät, jotka estivät meitä.

Ensimmäinen ongelma tyypillistä useimmille monoliitteille: liiketoimintalogiikan johdonmukaisuus. Kun kirjoitamme monoliittia, haluamme käyttää luokkiamme uudelleen, jotta emme kirjoita tarpeetonta koodia. Ja mikropalveluihin siirtyessä tästä tulee ongelma: kaikki koodit ovat varsin tiukasti kytkettyjä, ja palveluita on vaikea erottaa toisistaan.

Työn alkaessa arkistossa oli yli 500 projektia ja yli 700 tuhatta koodiriviä. Tämä on melko suuri päätös ja toinen ongelma. Sitä ei ollut mahdollista vain ottaa ja jakaa mikropalveluihin.

Kolmas ongelma — tarvittavan infrastruktuurin puute. Itse asiassa kopioimme lähdekoodin manuaalisesti palvelimille.

Kuinka siirtyä monoliitista mikropalveluihin


Mikropalvelujen tarjoaminen

Ensinnäkin totesimme heti itsellemme, että mikropalvelujen erottaminen on iteratiivinen prosessi. Meitä vaadittiin aina kehittämään liiketoimintaongelmia rinnakkain. Se, miten toteutamme tämän teknisesti, on jo meidän ongelmamme. Siksi valmistauduimme iteratiiviseen prosessiin. Se ei toimi millään muulla tavalla, jos sinulla on suuri sovellus, eikä se ole alun perin valmis kirjoitettavaksi uudelleen.

Mitä menetelmiä käytämme mikropalvelujen eristämiseen?

Ensimmäinen tapa — siirtää olemassa olevia moduuleja palveluina. Tässä suhteessa meillä oli onnea: siellä oli jo rekisteröityjä palveluita, jotka toimivat WCF-protokollalla. Ne jaettiin erillisiin kokoonpanoihin. Portit ne erikseen ja lisäsimme jokaiseen kokoonpanoon pienen kantoraketin. Se on kirjoitettu käyttämällä upeaa Topshelf-kirjastoa, jonka avulla voit ajaa sovellusta sekä palveluna että konsolina. Tämä on kätevää virheenkorjauksessa, koska ratkaisuun ei tarvita lisäprojekteja.

Palvelut yhdistettiin liiketoimintalogiikan mukaisesti, koska ne käyttivät yhteisiä kokoonpanoja ja työskentelivät yhteisen tietokannan kanssa. Niitä tuskin voi puhtaassa muodossaan kutsua mikropalveluiksi. Voisimme kuitenkin tarjota nämä palvelut erikseen, eri prosesseissa. Pelkästään tämä teki mahdolliseksi vähentää niiden vaikutusta toisiinsa, mikä vähensi ongelmaa rinnakkaisen kehityksen ja yhden epäonnistumisen vuoksi.

Kokoonpano isäntäkoneen kanssa on vain yksi koodirivi Program-luokassa. Piilotimme työtä Topshelfin kanssa apuluokkaan.

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

       }
    }
}

Toinen tapa allokoida mikropalvelut on: luoda niitä ratkaisemaan uusia ongelmia. Jos samaan aikaan monoliitti ei kasva, tämä on jo erinomaista, mikä tarkoittaa, että olemme menossa oikeaan suuntaan. Uusien ongelmien ratkaisemiseksi yritimme luoda erilliset palvelut. Jos sellainen mahdollisuus oli, loimme "kanonisempia" palveluita, jotka hallitsevat täysin omaa tietomalliaan, erillisen tietokannan.

Me, kuten monet, aloitimme todennus- ja valtuutuspalveluista. Ne sopivat tähän täydellisesti. Ne ovat itsenäisiä, yleensä niillä on erillinen tietomalli. He eivät itse ole vuorovaikutuksessa monoliitin kanssa, vain se kääntyy heidän puoleensa ratkaistakseen joitain ongelmia. Näiden palvelujen avulla voit aloittaa siirtymisen uuteen arkkitehtuuriin, korjata niiden infrastruktuuria, kokeilla joitain verkkokirjastoihin liittyviä lähestymistapoja jne. Organisaatiossamme ei ole tiimejä, jotka eivät voisi luoda todennuspalvelua.

Kolmas tapa allokoida mikropalvelutKäyttämämme on hieman meille ominaista. Tämä on liiketoimintalogiikan poistaminen käyttöliittymätasosta. Pääkäyttöliittymäsovelluksemme on työpöytä, se, kuten taustaohjelma, on kirjoitettu C#-kielellä. Kehittäjät tekivät ajoittain virheitä ja siirsivät käyttöliittymään logiikkaa, jonka olisi pitänyt olla taustajärjestelmässä ja käyttää uudelleen.

Jos katsot todellista esimerkkiä käyttöliittymäosan koodista, voit nähdä, että suurin osa tästä ratkaisusta sisältää todellista liiketoimintalogiikkaa, joka on hyödyllinen muissa prosesseissa, ei vain käyttöliittymälomakkeen rakentamisessa.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Todellinen käyttöliittymälogiikka on vain parilla viimeisellä rivillä. Siirsimme sen palvelimelle, jotta sitä voidaan käyttää uudelleen, mikä vähentää käyttöliittymää ja saavuttaa oikean arkkitehtuurin.

Neljäs ja tärkein tapa eristää mikropalvelut, joka mahdollistaa monoliitin vähentämisen, on olemassa olevien palvelujen poistaminen käsittelyn kera. Kun poistamme olemassa olevat moduulit sellaisenaan, tulos ei aina ole kehittäjien mieleen ja liiketoimintaprosessi on voinut vanhentua toiminnallisuuden luomisen jälkeen. Refaktoroinnilla voimme tukea uutta liiketoimintaprosessia, koska liiketoiminnan vaatimukset muuttuvat jatkuvasti. Voimme parantaa lähdekoodia, poistaa tunnetut viat ja luoda paremman tietomallin. Hyötyjä kertyy monia.

Palvelujen erottaminen käsittelystä liittyy erottamattomasti rajatun kontekstin käsitteeseen. Tämä on Domain Driven Designin konsepti. Se tarkoittaa aluemallin osaa, jossa kaikki yhden kielen termit on määritelty yksilöllisesti. Katsotaanpa esimerkkiä vakuutusten ja laskujen kontekstista. Meillä on monoliittinen sovellus, ja meidän on työskenneltävä tilin kanssa vakuutuksessa. Odotamme kehittäjän löytävän olemassa olevan tililuokan toisesta kokoonpanosta, viittaavan siihen vakuutusluokasta, ja meillä on toimiva koodi. DRY-periaatetta kunnioitetaan, tehtävä suoritetaan nopeammin käyttämällä olemassa olevaa koodia.

Tuloksena käy ilmi, että tilien ja vakuutuksen kontekstit liittyvät toisiinsa. Uusien vaatimusten ilmaantuessa tämä kytkentä häiritsee kehitystä ja lisää monimutkaisempaa jo ennestään monimutkaista liiketoimintalogiikkaa. Tämän ongelman ratkaisemiseksi sinun on löydettävä koodin kontekstien väliset rajat ja poistettava niiden rikkomukset. Esimerkiksi vakuutusasiassa on täysin mahdollista, että 20-numeroinen keskuspankkitilinumero ja tilin avauspäivämäärä riittää.

Erottaaksemme nämä rajalliset kontekstit toisistaan ​​ja aloittaaksemme mikropalvelujen erottamisen monoliittisesta ratkaisusta, käytimme lähestymistapaa, kuten ulkoisten API:iden luomista sovelluksen sisällä. Jos tiesimme, että jostain moduulista pitäisi tulla mikropalvelu, jotenkin prosessin sisällä muokattuna, soitettiin heti ulkoisten kutsujen kautta toiseen rajoitettuun kontekstiin kuuluvaa logiikkaa. Esimerkiksi REST:n tai WCF:n kautta.

Päätimme vakaasti, että emme välttele koodia, joka vaatisi hajautettuja tapahtumia. Meidän tapauksessamme tämän säännön noudattaminen osoittautui melko helpoksi. Emme ole vielä kohdanneet tilanteita, joissa tiukkoja hajautettuja transaktioita todella tarvitaan - lopullinen johdonmukaisuus moduulien välillä on aivan riittävä.

Katsotaanpa konkreettista esimerkkiä. Meillä on orkesterikonsepti - putki, joka käsittelee "sovelluksen" kokonaisuuden. Hän luo vuorollaan asiakkaan, tilin ja pankkikortin. Jos asiakas ja tili luodaan onnistuneesti, mutta kortin luominen epäonnistuu, sovellus ei siirry onnistuneeseen tilaan ja pysyy tilassa "korttia ei luotu". Jatkossa taustatoiminta poimii sen ja viimeistelee sen. Järjestelmä on ollut epäjohdonmukaisessa tilassa jo jonkin aikaa, mutta olemme yleisesti ottaen tyytyväisiä tähän.

Jos tulee tilanne, jossa on tarpeen tallentaa osa tiedoista johdonmukaisesti, ryhdymme todennäköisesti palvelun yhdistämiseen, jotta se voidaan käsitellä yhdessä prosessissa.

Katsotaanpa esimerkkiä mikropalvelun allokoinnista. Miten saat sen tuotantoon suhteellisen turvallisesti? Tässä esimerkissä meillä on järjestelmästä erillinen osa - palkanlaskentapalvelumoduuli, jonka yhdestä koodiosista haluaisimme tehdä mikropalvelun.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Ensinnäkin luomme mikropalvelun kirjoittamalla koodin uudelleen. Parannamme joitakin kohtia, joihin emme olleet tyytyväisiä. Toteutamme uusia liiketoimintavaatimuksia asiakkaalta. Lisäämme käyttöliittymän ja taustajärjestelmän väliseen yhteyteen API-yhdyskäytävän, joka tarjoaa soitonsiirron.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Seuraavaksi vapautamme tämän kokoonpanon käyttöön, mutta pilottitilassa. Suurin osa käyttäjistämme työskentelee edelleen vanhojen liiketoimintaprosessien parissa. Uusille käyttäjille kehitämme monoliittisesta sovelluksesta uutta versiota, joka ei enää sisällä tätä prosessia. Pohjimmiltaan meillä on pilottina toimivan monoliitin ja mikropalvelun yhdistelmä.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Onnistuneen pilotin myötä ymmärrämme, että uusi konfiguraatio on todellakin toimiva, voimme poistaa yhtälöstä vanhan monoliitin ja jättää uuden kokoonpanon vanhan ratkaisun tilalle.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Kaiken kaikkiaan käytämme lähes kaikkia olemassa olevia menetelmiä monoliitin lähdekoodin jakamiseen. Niiden kaikkien avulla voimme pienentää sovelluksen osien kokoa ja kääntää ne uusiin kirjastoihin, mikä parantaa lähdekoodia.

Työskentely tietokannan kanssa


Tietokanta voidaan jakaa huonommin kuin lähdekoodi, koska se ei sisällä vain nykyistä skeemaa, vaan myös kertynyttä historiatietoa.

Tietokannassamme, kuten monilla muillakin, oli toinen tärkeä haittapuoli - sen valtava koko. Tämä tietokanta suunniteltiin monoliitin monimutkaisen liiketoimintalogiikan ja erilaisten rajoitettujen kontekstien taulukoiden välisten suhteiden mukaan.

Meidän tapauksessamme kaikkien ongelmien (suuri tietokanta, monet yhteydet, joskus epäselvät taulukoiden väliset rajat) lisäksi ilmeni monissa suurissa projekteissa esiintyvä ongelma: jaetun tietokantamallin käyttö. Tiedot otettiin taulukoista läpi näkymän, replikoinnin kautta ja lähetettiin muihin järjestelmiin, joissa tätä replikointia tarvittiin. Tämän seurauksena emme voineet siirtää taulukoita erilliseen skeemaan, koska niitä käytettiin aktiivisesti.

Sama jako rajoitettuihin konteksteihin koodissa auttaa meitä erottamisessa. Se antaa meille yleensä melko hyvän käsityksen siitä, kuinka jaamme tiedot tietokantatasolla. Ymmärrämme, mitkä taulukot kuuluvat yhteen rajoitettuun kontekstiin ja mitkä toiseen.

Käytimme kahta globaalia tietokannan osiointimenetelmää: olemassa olevien taulukoiden osiointia ja osiointia prosessoimalla.

Olemassa olevien taulukoiden jakaminen on hyvä tapa käyttää, jos tietorakenne on hyvä, täyttää liiketoiminnan vaatimukset ja kaikki ovat siihen tyytyväisiä. Tässä tapauksessa voimme erottaa olemassa olevat taulukot erilliseksi skeemaksi.

Jalostusosastoa tarvitaan, kun liiketoimintamalli on muuttunut suuresti, eivätkä taulukot enää tyydytä meitä ollenkaan.

Olemassa olevien taulukoiden jakaminen. Meidän on päätettävä, mitä erotamme. Ilman tätä tietoa mikään ei toimi, ja tässä koodissa olevien rajoitettujen kontekstien erottaminen auttaa meitä. Yleensä jos ymmärrät kontekstien rajat lähdekoodissa, käy selväksi, mitkä taulukot tulisi sisällyttää laitoksen luetteloon.

Kuvitellaan, että meillä on ratkaisu, jossa kaksi monoliittimoduulia ovat vuorovaikutuksessa yhden tietokannan kanssa. Meidän on varmistettava, että vain yksi moduuli on vuorovaikutuksessa erotettujen taulukkojen osion kanssa ja toinen alkaa olla vuorovaikutuksessa sen kanssa API:n kautta. Aluksi riittää, että vain tallennus suoritetaan API:n kautta. Tämä on välttämätön edellytys, jotta voimme puhua mikropalvelujen riippumattomuudesta. Lukuyhteydet voivat säilyä niin kauan kuin ei ole suuria ongelmia.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Seuraava askel on, että voimme erottaa koodin osan, joka toimii erillisillä taulukoilla, prosessoinnilla tai ilman, erilliseksi mikropalveluksi ja ajaa sitä erillisessä prosessissa, säiliössä. Tämä on erillinen palvelu, jossa on yhteys monoliittitietokantaan ja niihin taulukoihin, jotka eivät liity siihen suoraan. Monoliitti on edelleen vuorovaikutuksessa lukemista varten irrotettavan osan kanssa.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Myöhemmin poistamme tämän yhteyden, eli myös monoliittisen sovelluksen tietojen lukeminen erillisistä taulukoista siirtyy API:lle.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Seuraavaksi valitsemme yleisestä tietokannasta ne taulukot, joiden kanssa vain uusi mikropalvelu toimii. Voimme siirtää taulukot erilliseen skeemaan tai jopa erilliseen fyysiseen tietokantaan. Lukuyhteys mikropalvelun ja monoliittitietokannan välillä on edelleen, mutta ei ole mitään hätää, tässä kokoonpanossa se voi elää melko pitkään.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Viimeinen vaihe on poistaa kaikki liitännät kokonaan. Tässä tapauksessa meidän on ehkä siirrettävä tiedot päätietokannasta. Joskus haluamme käyttää uudelleen joitakin ulkoisista järjestelmistä replikoituja tietoja tai hakemistoja useissa tietokannoissa. Tätä tapahtuu meille ajoittain.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Käsittelyosasto. Tämä menetelmä on hyvin samanlainen kuin ensimmäinen, vain käänteisessä järjestyksessä. Varaamme välittömästi uuden tietokannan ja uuden mikropalvelun, joka on vuorovaikutuksessa monoliitin kanssa API:n kautta. Mutta samaan aikaan jäljellä on joukko tietokantataulukoita, jotka haluamme poistaa tulevaisuudessa. Emme enää tarvitse sitä, vaan korvasimme sen uuteen malliin.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Jotta tämä järjestelmä toimisi, tarvitsemme todennäköisesti siirtymäkauden.

Silloin on kaksi mahdollista lähestymistapaa.

Ensimmäinen: kopioimme kaikki tiedot uudessa ja vanhassa tietokannassa. Tässä tapauksessa meillä on tietojen redundanssi ja synkronointiongelmia saattaa ilmetä. Mutta voimme ottaa kaksi eri asiakasta. Toinen toimii uuden version kanssa, toinen vanhan kanssa.

Toinen: jaamme tiedot joidenkin liiketoimintakriteerien mukaan. Esimerkiksi järjestelmässämme oli 5 tuotetta, jotka oli tallennettu vanhaan tietokantaan. Sijoitamme kuudennen uuteen liiketoimintatehtävään uuteen tietokantaan. Mutta tarvitsemme API-yhdyskäytävän, joka synkronoi nämä tiedot ja näyttää asiakkaalle, mistä ja mistä saa.

Molemmat lähestymistavat toimivat, valitse tilanteen mukaan.

Kun olemme varmoja, että kaikki toimii, monoliitin osa, joka toimii vanhojen tietokantarakenteiden kanssa, voidaan poistaa käytöstä.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Viimeinen vaihe on poistaa vanhat tietorakenteet.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Yhteenvetona voidaan sanoa, että meillä on ongelmia tietokannan kanssa: sen kanssa on vaikea työskennellä lähdekoodiin verrattuna, sitä on vaikeampi jakaa, mutta se voidaan ja pitäisi tehdä. Olemme löytäneet tapoja, joilla voimme tehdä tämän melko turvallisesti, mutta virheitä on silti helpompi tehdä datan kuin lähdekoodin kanssa.

Työskentely lähdekoodin kanssa


Tältä lähdekoodikaavio näytti, kun aloimme analysoida monoliittista projektia.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Se voidaan jakaa karkeasti kolmeen kerrokseen. Tämä on kerros käynnistettyjä moduuleja, laajennuksia, palveluita ja yksittäisiä toimintoja. Itse asiassa nämä olivat sisääntulopisteitä monoliittisessa ratkaisussa. Kaikki ne suljettiin tiiviisti yhteisellä kerroksella. Siinä oli bisneslogiikka, että palvelut jakavat ja paljon yhteyksiä. Jokainen palvelu ja laajennus käytti jopa 10 tai useampaa yleistä kokoonpanoa niiden koosta ja kehittäjien omantunnon mukaan.

Meillä oli onni, että meillä oli infrastruktuurikirjastoja, joita voitiin käyttää erikseen.

Joskus syntyi tilanne, kun jotkut yleiset objektit eivät varsinaisesti kuuluneet tähän kerrokseen, vaan olivat infrastruktuurikirjastoja. Tämä ratkesi nimeämällä.

Suurin huolenaihe oli rajalliset kontekstit. Tapahtui, että 3-4 kontekstia sekoitettiin yhteen yhteiseen kokoonpanoon ja käytettiin toisiaan samoissa liiketoimintatoiminnoissa. Oli tarpeen ymmärtää, mihin tämä voidaan jakaa ja mitä rajoja pitkin, ja mitä tehdä seuraavaksi kartoittamalla tämä jako lähdekoodikokoonpanoihin.

Olemme laatineet useita sääntöjä koodinjakoprosessille.

Ensimmäisen: Emme enää halunneet jakaa liiketoimintalogiikkaa palveluiden, toimintojen ja laajennusten välillä. Halusimme tehdä liiketoimintalogiikasta itsenäistä mikropalveluissa. Mikropalveluja sen sijaan ajatellaan ihanteellisesti palveluina, jotka ovat olemassa täysin itsenäisesti. Mielestäni tämä lähestymistapa on hieman turhaa ja vaikeasti toteutettavissa, koska esimerkiksi C#:n palvelut yhdistetään joka tapauksessa standardikirjastolla. Järjestelmämme on kirjoitettu C#-kielellä, emme ole vielä käyttäneet muita tekniikoita. Siksi päätimme, että meillä on varaa käyttää yhteisiä teknisiä kokoonpanoja. Tärkeintä on, että ne eivät sisällä liikelogiikan fragmentteja. Jos käytät mukavuuskäärettä käyttämäsi ORM:n päällä, sen kopioiminen palvelusta palveluun on erittäin kallista.

Tiimimme on toimialuelähtöisen suunnittelun fani, joten sipuliarkkitehtuuri sopi meille loistavasti. Palvelujemme perustana ei ole tiedonkäyttökerros, vaan toimialuelogiikalla varustettu kokoonpano, joka sisältää vain liiketoimintalogiikkaa ja jolla ei ole yhteyksiä infrastruktuuriin. Samalla voimme itsenäisesti muokata toimialuekokoonpanoa puitteisiin liittyvien ongelmien ratkaisemiseksi.

Tässä vaiheessa kohtasimme ensimmäisen vakavan ongelmamme. Palvelun piti viitata yhteen toimialuekokoonpanoon, logiikasta haluttiin tehdä riippumaton, ja DRY-periaate hankaloitti meitä tässä suuresti. Kehittäjät halusivat käyttää uudelleen viereisten kokoonpanojen luokkia päällekkäisyyksien välttämiseksi, ja sen seurauksena verkkotunnuksia alettiin linkittää uudelleen yhteen. Analysoimme tuloksia ja päätimme, että ehkä ongelma on myös lähdekoodin tallennuslaitteen alueella. Meillä oli suuri arkisto, joka sisälsi kaiken lähdekoodin. Koko projektin ratkaisu oli erittäin vaikea koota paikalliselle koneelle. Siksi projektin osiin luotiin erilliset pienet ratkaisut, eikä kukaan kieltänyt niihin lisäämästä jotain yhteistä tai domain-kokoonpanoa ja uudelleenkäyttöä. Ainoa työkalu, joka ei antanut meidän tehdä tätä, oli koodin tarkistus. Mutta joskus se myös epäonnistui.

Sitten aloimme siirtyä malliin, jossa oli erilliset arkistot. Liiketoimintalogiikka ei enää virtaa palvelusta palveluun, toimialueet ovat todella itsenäistyneet. Rajoitettuja konteksteja tuetaan selkeämmin. Kuinka käytämme infrastruktuurikirjastoja uudelleen? Erotimme ne erilliseen arkistoon ja laitoimme sitten Nuget-paketteihin, jotka laitoimme Artifactoryyn. Kaikissa muutoksissa kokoaminen ja julkaiseminen tapahtuu automaattisesti.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Palvelumme alkoivat viitata sisäisiin infrastruktuuripaketteihin samalla tavalla kuin ulkoiset. Lataamme ulkoisia kirjastoja Nugetista. Työskentelyksemme Artifactoryn kanssa, johon sijoitimme nämä paketit, käytimme kahta paketinhallintaa. Pienissä arkistoissa käytimme myös Nugetia. Useita palveluita sisältävissä arkistoissa käytimme Paketia, joka tarjoaa enemmän versioiden yhdenmukaisuutta moduulien välillä.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Siten työskentelemällä lähdekoodin parissa, muuttamalla hieman arkkitehtuuria ja erottamalla arkistot, teemme palveluistamme itsenäisempiä.

Infrastruktuuri-ongelmat


Suurin osa mikropalveluihin siirtymisen haitoista liittyy infrastruktuuriin. Tarvitset automaattisen käyttöönoton, tarvitset uusia kirjastoja infrastruktuurin ylläpitämiseen.

Manuaalinen asennus ympäristöihin

Aluksi asensimme ympäristöratkaisun manuaalisesti. Tämän prosessin automatisoimiseksi loimme CI/CD-liukuhihnan. Valitsimme jatkuvan toimitusprosessin, koska jatkuva käyttöönotto ei ole meille vielä hyväksyttävää liiketoimintaprosessien kannalta. Siksi lähettäminen käyttöön tapahtuu painikkeella ja testaus - automaattisesti.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Käytämme Atlassiania, Bitbucketia lähdekoodin tallentamiseen ja Bamboota rakentamiseen. Haluamme kirjoittaa rakennusskriptejä Cakessa, koska se on sama kuin C#. Valmiit paketit tulevat Artifactoryyn ja Ansible pääsee automaattisesti testipalvelimille, minkä jälkeen niitä voidaan testata välittömästi.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Erillinen kirjaus


Aikoinaan yksi monoliitin ajatuksista oli jaettu hakkuu. Meidän piti myös ymmärtää, mitä tehdä levyillä oleville yksittäisille lokeille. Lokimme kirjoitetaan tekstitiedostoihin. Päätimme käyttää tavallista ELK-pinoa. Emme kirjoittaneet ELK:lle suoraan palveluntarjoajien kautta, vaan päätimme, että muokkaamme tekstilokeja ja kirjoitamme niihin tunnisteena jäljitystunnuksen ja lisäämme palvelun nimen, jotta nämä lokit voidaan jäsentää myöhemmin.

Siirtyminen monoliitista mikropalveluihin: historiaa ja käytäntöä

Filebeatin avulla saamme mahdollisuuden kerätä lokit palvelimilta, sitten muuttaa ne, rakentaa Kibanan avulla kyselyitä käyttöliittymässä ja nähdä kuinka puhelu sujui palveluiden välillä. Trace ID auttaa tässä paljon.

Testaukseen ja virheenkorjaukseen liittyvät palvelut


Aluksi emme täysin ymmärtäneet, miten kehitettävien palvelujen virheenkorjaus tehdään. Kaikki oli yksinkertaista monoliitin kanssa; suoritimme sen paikallisella koneella. Aluksi he yrittivät tehdä samaa mikropalveluiden kanssa, mutta joskus yhden mikropalvelun käynnistämiseksi kokonaan sinun on käynnistettävä useita muita, mikä on hankalaa. Ymmärsimme, että meidän on siirryttävä malliin, jossa jätämme paikalliselle koneelle vain palvelun tai palvelut, jotka haluamme korjata. Loput palvelut käytetään palvelimilta, jotka vastaavat kokoonpanoa prod. Viankorjauksen jälkeen testauksen aikana jokaisesta tehtävästä vain muuttuneet palvelut lähetetään testipalvelimelle. Ratkaisua siis testataan siinä muodossa, jossa se tulee jatkossa tuotantoon.

On palvelimia, jotka suorittavat vain palveluiden tuotantoversioita. Näitä palvelimia tarvitaan häiriötilanteissa, toimituksen tarkistamiseen ennen käyttöönottoa ja sisäiseen koulutukseen.

Olemme lisänneet automaattisen testausprosessin käyttämällä suosittua Specflow-kirjastoa. Testit suoritetaan automaattisesti NUnitin avulla välittömästi Ansiblen käyttöönoton jälkeen. Jos tehtäväkattavuus on täysin automaattinen, manuaalista testausta ei tarvita. Vaikka joskus ylimääräistä manuaalista testausta tarvitaan edelleen. Käytämme Jirassa tageja määrittääksemme, mitkä testit suoritetaan tietyn ongelman yhteydessä.

Lisäksi kuormitustestauksen tarve on lisääntynyt, aiemmin sitä on tehty vain harvoin. Käytämme JMeteriä testien suorittamiseen, InfluxDB:tä niiden tallentamiseen ja Grafanaa prosessikaavioiden rakentamiseen.

Mitä olemme saavuttaneet?


Ensinnäkin pääsimme eroon "vapauttamisen" käsitteestä. Kaksi kuukautta kestäneet hirvittävät julkaisut ovat poissa, kun tämä kolossi otettiin käyttöön tuotantoympäristössä, mikä häiritsi tilapäisesti liiketoimintaprosesseja. Nyt otamme palvelut käyttöön keskimäärin 1,5 päivän välein ryhmitellen ne, koska ne otetaan käyttöön hyväksynnän jälkeen.

Järjestelmässämme ei ole kohtalokkaita vikoja. Jos julkaisemme mikropalvelun, jossa on virhe, siihen liittyvä toiminnallisuus katkeaa, eikä se vaikuta muihin toimintoihin. Tämä parantaa huomattavasti käyttökokemusta.

Voimme hallita käyttöönottomallia. Voit tarvittaessa valita palveluryhmiä muusta ratkaisusta erikseen.

Lisäksi olemme merkittävästi vähentäneet ongelmaa suurella parannusjonolla. Meillä on nyt erilliset tuoteryhmät, jotka työskentelevät osan palveluista itsenäisesti. Scrum-prosessi sopii jo hyvin tähän. Tietyllä tiimillä voi olla erillinen tuoteomistaja, joka määrittää sille tehtäviä.

Yhteenveto

  • Mikropalvelut soveltuvat hyvin monimutkaisten järjestelmien hajottamiseen. Prosessin aikana alamme ymmärtää, mitä järjestelmässämme on, mitä rajoitettuja konteksteja siellä on, missä niiden rajat ovat. Näin voit jakaa parannukset oikein moduulien kesken ja estää koodin sekaannukset.
  • Mikropalvelut tarjoavat organisaation etuja. Niistä puhutaan usein vain arkkitehtuurina, mutta mitä tahansa arkkitehtuuria tarvitaan liiketoiminnan tarpeiden ratkaisemiseen, ei yksinään. Siksi voidaan sanoa, että mikropalvelut sopivat hyvin pienten ryhmien ongelmien ratkaisemiseen, koska Scrum on nyt erittäin suosittu.
  • Erottaminen on iteratiivinen prosessi. Et voi ottaa hakemusta ja vain jakaa sitä mikropalveluihin. Tuloksena oleva tuote ei todennäköisesti ole toimiva. Mikropalveluita omistettaessa on hyödyllistä kirjoittaa olemassa oleva perintö uudelleen eli muuttaa se sellaiseksi koodiksi, josta pidämme ja joka vastaa paremmin liiketoiminnan tarpeita toimivuuden ja nopeuden suhteen.

    Pieni varoitus: Mikropalveluihin siirtymisen kustannukset ovat melkoiset. Infrastruktuuriongelman ratkaiseminen yksin kesti kauan. Joten jos sinulla on pieni sovellus, joka ei vaadi erityistä skaalausta, ellei sinulla ole suuri määrä asiakkaita, jotka kilpailevat tiimisi huomiosta ja ajasta, mikropalvelut eivät välttämättä ole sitä, mitä tarvitset tänään. Se on melko kallista. Jos aloitat prosessin mikropalveluilla, kustannukset ovat aluksi korkeammat kuin jos aloitat saman projektin monoliitin kehittämisellä.

    PS Tunteisempi tarina (ja ikään kuin sinulle henkilökohtaisesti) - mukaan linkki.
    Tässä on raportin täysi versio.

Lähde: will.com

Lisää kommentti