Historien om Dodo IS-arkitekturen: En tidlig monolitt

Eller ethvert ulykkelig selskap med en monolitt er ulykkelig på sin egen måte.

Utviklingen av Dodo IS-systemet begynte umiddelbart, som Dodo Pizza-virksomheten, i 2011. Det var basert på ideen om fullstendig og total digitalisering av forretningsprosesser, og på egen hånd, som allerede da i 2011 forårsaket mange spørsmål og skepsis. Men i 9 år nå har vi fulgt denne veien - med vår egen utvikling, som begynte med en monolitt.

Denne artikkelen er et "svar" på spørsmålene "Hvorfor omskrive arkitekturen og gjøre slike store og langsiktige endringer?" tilbake til forrige artikkel "History of the Dodo IS Architecture: Way of the Back Office". Jeg starter med hvordan utviklingen av Dodo IS begynte, hvordan den opprinnelige arkitekturen så ut, hvordan nye moduler dukket opp, og på grunn av hvilke problemer store endringer måtte gjøres.

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Artikkelserie "Hva er Dodo IS?" forteller om:

  1. Tidlig monolitt i Dodo IS (2011-2015). (du er her)

  2. Back Office Path: Separate baser og buss.

  3. Klientsideveien: fasade over basen (2016-2017). (I prosess...)

  4. Historien om ekte mikrotjenester. (2018-2019). (I prosess...)

  5. Ferdig saging av monolitten og stabilisering av arkitekturen. (I prosess...)

Opprinnelig arkitektur

I 2011 så Dodo IS-arkitekturen slik ut:

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Den første modulen i arkitekturen er ordreaksept. Forretningsprosessen var:

  • klienten ringer pizzeriaen;

  • lederen tar telefonen;

  • godtar en bestilling via telefon;

  • fyller den parallelt i ordreakseptgrensesnittet: informasjon om klienten, data om ordredetaljer, leveringsadresse tas i betraktning. 

Grensesnittet til informasjonssystemet så omtrent slik ut ...

Første versjon fra oktober 2011:

Litt forbedret i januar 2012

Dodo Pizza Informasjonssystem Levering Pizza Restaurant

Ressursene for utviklingen av den første ordremottaksmodulen var begrenset. Vi måtte gjøre mye, raskt og med et lite team. Et lite team er 2 utviklere som la grunnlaget for hele det fremtidige systemet.

Deres første avgjørelse avgjorde skjebnen til teknologistabelen:

  • Backend på ASP.NET MVC, C#-språk. Utviklerne var dotnetchiki, denne stabelen var kjent og hyggelig for dem.

  • Frontend på Bootstrap og JQuery: brukergrensesnitt på selvskrevne stiler og skript. 

  • MySQL-database: ingen lisenskostnader, enkel å bruke.

  • Servere på Windows Server, fordi .NET da kun kunne være under Windows (vi vil ikke diskutere Mono).

Fysisk ble alt dette uttrykt i "dediken på vertskapet". 

Ordreinntak applikasjonsarkitektur

Da snakket alle allerede om mikrotjenester, og SOA ble brukt i store prosjekter i 5 år, for eksempel ble WCF utgitt i 2006. Men så valgte de en pålitelig og velprøvd løsning.

Her er det.

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Asp.Net MVC er Razor, som, på forespørsel fra et skjema eller fra en klient, gjengir en HTML-side med servergjengivelse. På klienten viser CSS- og JS-skript allerede informasjon og utfører om nødvendig AJAX-forespørsler gjennom JQuery.

Forespørsler på serveren havner i *Controller-klassene, hvor behandlingen og genereringen av den endelige HTML-siden skjer i metoden. Kontrollere sender forespørsler til et lag med logikk kalt *Tjenester. Hver av tjenestene tilsvarte et eller annet aspekt av virksomheten:

  • For eksempel ga DepartmentStructureService ut informasjon om pizzeriaer, om avdelinger. En avdeling er en gruppe pizzeriaer som drives av en enkelt franchisetaker.

  • ReceivingOrdersService aksepterte og beregnet sammensetningen av bestillingen.

  • Og SmsService sendte SMS ved å ringe API-tjenester for å sende SMS.

Tjenester behandlet data fra databasen, lagret forretningslogikk. Hver tjeneste hadde ett eller flere *Repositories med riktig navn. De inneholdt allerede spørringer til lagrede prosedyrer i databasen og et lag med kartleggere. Det var forretningslogikk i lagrene, spesielt mye i de som utstedte rapporteringsdata. ORM ble ikke brukt, alle stolte på håndskrevet sql. 

Det var også et lag av domenemodellen og vanlige hjelpeklasser, for eksempel Order-klassen som lagret bestillingen. På samme sted, i laget, var det en hjelper for å konvertere visningsteksten i henhold til den valgte valutaen.

Alt dette kan representeres av en slik modell:

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Bestill måte

Vurder en forenklet innledende måte å opprette en slik ordre på.

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Opprinnelig var nettstedet statisk. Den hadde priser på den, og på toppen - et telefonnummer og påskriften "Hvis du vil ha pizza - ring nummeret og bestill." For å bestille, må vi implementere en enkel flyt: 

  • Kunden besøker en statisk side med priser, velger produkter og ringer nummeret som er oppført på siden.

  • Kunden navngir produktene de ønsker å legge til bestillingen.

  • Oppgir hans adresse og navn.

  • Operatøren godtar bestillingen.

  • Bestillingen vises i grensesnittet for aksepterte bestillinger.

Det hele starter med å vise menyen. En pålogget bruker-operatør aksepterer kun én ordre om gangen. Derfor kan kladdevognen lagres i økten hans (brukerens økt lagres i minnet). Det er et vognobjekt som inneholder produkter og kundeinformasjon.

Kunden navngir produktet, operatøren klikker videre + ved siden av produktet, og en forespørsel sendes til serveren. Informasjon om produktet trekkes ut fra databasen og informasjon om produktet legges i handlekurven.

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Note. Ja, her kan du ikke trekke produktet fra databasen, men overføre det fra frontend. Men for klarhetens skyld viste jeg nøyaktig banen fra databasen. 

Deretter skriver du inn adressen og navnet til klienten. 

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Når du klikker "Opprett bestilling":

  • Forespørselen sendes til OrderController.SaveOrder().

  • Vi får Handlevogn fra økten, det er produkter i den mengde vi trenger.

  • Vi supplerer handlekurven med informasjon om klienten og sender den til AddOrder-metoden til ReceivingOrderService-klassen, hvor den lagres i databasen. 

  • Databasen har tabeller med ordren, sammensetningen av ordren, klienten, og de er alle koblet sammen.

  • Bestillingsvisningsgrensesnittet går og trekker ut de siste bestillingene og viser dem.

Nye moduler

Å ta bestillingen var viktig og nødvendig. Du kan ikke drive pizzavirksomhet hvis du ikke har en ordre å selge. Derfor begynte systemet å skaffe seg funksjonalitet - omtrent fra 2012 til 2015. I løpet av denne tiden dukket det opp mange forskjellige blokker av systemet, som jeg vil kalle moduler, i motsetning til begrepet tjeneste eller produkt. 

En modul er et sett med funksjoner som er forent av et felles forretningsmål. Samtidig er de fysisk i samme applikasjon.

Moduler kan kalles systemblokker. Dette er for eksempel en rapporteringsmodul, admin-grensesnitt, matsporer på kjøkkenet, autorisasjon. Disse er alle forskjellige brukergrensesnitt, noen har til og med forskjellige visuelle stiler. Samtidig er alt innenfor rammen av én applikasjon, én løpende prosess. 

Teknisk sett ble modulene designet som Area (en slik idé forble til og med inne asp.net kjerne). Det var separate filer for frontend, modeller, samt deres egne kontrollerklasser. Som et resultat ble systemet transformert fra denne ...

Historien om Dodo IS-arkitekturen: En tidlig monolitt

...inn i dette:

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Noen moduler er implementert av separate nettsteder (kjørbart prosjekt), på grunn av en helt egen funksjonalitet og delvis på grunn av en litt separat, mer fokusert utvikling. Dette:

  • Side - første versjon nettstedet dodopizza.ru.

  • Eksport: laster opp rapporter fra Dodo IS for 1C. 

  • Personlig - personlig konto til den ansatte. Den ble utviklet separat og har sitt eget inngangspunkt og eget design.

  • fs — et prosjekt for hosting av statikk. Senere flyttet vi bort fra det, og flyttet all statikk til Akamai CDN. 

Resten av blokkene var i BackOffice-applikasjonen. 

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Navneforklaring:

  • Kasserer - Restaurantkasserer.

  • ShiftManager - grensesnitt for rollen "Shift Manager": driftsstatistikk over pizzeriasalg, muligheten til å sette produkter på stopplisten, endre rekkefølgen.

  • OfficeManager - grensesnitt for rollene "Pizzeria Manager" og "Franchisetaker". Her er samlet funksjoner for å sette opp en pizzeria, dens bonuskampanjer, mottak og arbeid med ansatte, rapporter.

  • PublicScreens - grensesnitt for TV-er og nettbrett som henger i pizzeriaer. TV-er viser menyer, reklameinformasjon, ordrestatus ved levering. 

De brukte et felles tjenestelag, en felles Dodo.Core-domeneklasseblokk og en felles base. Noen ganger kunne de fortsatt lede langs overgangene til hverandre. Inkludert individuelle nettsteder, for eksempel dodopizza.ru eller personal.dodopizza.ru, gikk til generelle tjenester.

Da nye moduler dukket opp, prøvde vi å gjenbruke den allerede opprettede tjenestekoden, lagrede prosedyrer og tabeller i databasen maksimalt. 

For en bedre forståelse av omfanget av modulene laget i systemet, er her et diagram fra 2012 med utviklingsplaner:

Historien om Dodo IS-arkitekturen: En tidlig monolitt

I 2015 var alt på kartet og enda mer var i produksjon.

  • Ordreaksept har vokst til en egen blokk av kontaktsenteret, hvor bestillingen aksepteres av operatøren.

  • Det var offentlige skjermer med menyer og informasjon hengende i pizzeriaer.

  • Kjøkkenet har en modul som automatisk spiller av talemeldingen «Ny pizza» når en ny bestilling kommer, og også skriver ut en faktura til budet. Dette forenkler prosessene på kjøkkenet, gjør at ansatte ikke kan bli distrahert av et stort antall enkle operasjoner.

  • Leveringsenheten ble en egen Delivery Checkout, hvor bestillingen ble utstedt til budet som tidligere hadde tatt skiftet. Arbeidstiden hans ble tatt med i lønnsberegningen. 

Parallelt, fra 2012 til 2015, dukket det opp mer enn 10 utviklere, 35 pizzeriaer åpnet, distribuerte systemet til Romania og forberedte åpningen av utsalgssteder i USA. Utviklerne tok seg ikke lenger av alle oppgavene, men ble delt inn i team. hver spesialiserte seg på sin egen del av systemet. 

Problemer

Inkludert på grunn av arkitekturen (men ikke bare).

Kaos i basen

En base er praktisk. Konsistens kan oppnås i den, og på bekostning av verktøy innebygd i relasjonsdatabaser. Å jobbe med det er kjent og praktisk, spesielt hvis det er få tabeller og lite data.

Men over 4 års utvikling viste databasen seg å ha rundt 600 tabeller, 1500 lagrede prosedyrer, hvorav mange også hadde logikk. Dessverre, lagrede prosedyrer gir ikke mye fordel når du arbeider med MySQL. De bufres ikke av basen, og lagring av logikk i dem kompliserer utvikling og feilsøking. Gjenbruk av kode er også vanskelig.

Mange tabeller hadde ikke passende indekser, et sted, tvert imot, var det mange indekser, noe som gjorde det vanskelig å sette inn. Det var nødvendig å endre rundt 20 tabeller - transaksjonen for å opprette en ordre kunne ta omtrent 3-5 sekunder. 

Dataene i tabellene var ikke alltid i den mest hensiktsmessige formen. Et sted var det nødvendig å gjøre denormalisering. En del av de regelmessig mottatte dataene var i en kolonne i form av en XML-struktur, dette økte utførelsestiden, forlenget spørringene og kompliserte utviklingen.

Til de samme bordene ble produsert veldig heterogene forespørsler. Populære bord led spesielt, som tabellen nevnt ovenfor. ordrer eller tabeller pizzeria. De ble brukt til å vise operative grensesnitt på kjøkkenet, analyser. Et annet nettsted kontaktet dem (dodopizza.ru), hvor det til enhver tid plutselig kunne komme mange forespørsler. 

Dataene ble ikke samlet og mange beregninger fant sted i farten ved hjelp av basen. Dette skapte unødvendige beregninger og ekstra belastning. 

Ofte gikk koden til databasen når den ikke kunne ha gjort det. Et sted var det ikke nok bulkoperasjoner, et sted ville det være nødvendig å spre én forespørsel til flere gjennom koden for å få fart på og øke påliteligheten. 

Sammenheng og tilsløring i kode

Moduler som skulle stå for sin del av virksomheten gjorde det ikke ærlig. Noen av dem hadde duplisering av funksjoner for roller. For eksempel måtte en lokal markedsfører som er ansvarlig for nettverkets markedsføringsaktivitet i byen hans bruke både «Admin»-grensesnittet (for å lage kampanjer) og «Office Manager»-grensesnittet (for å se virkningen av kampanjer på virksomheten). Selvfølgelig brukte begge modulene den samme tjenesten som fungerte med bonuskampanjer.

Tjenester (klasser innenfor et monolittisk stort prosjekt) kan ringe hverandre for å berike dataene deres.

Med selve modellklassene som lagrer data, arbeidet i koden ble utført annerledes. Et sted var det konstruktører som det var mulig å spesifisere nødvendige felt gjennom. Et sted ble dette gjort gjennom offentlige eiendommer. Å hente og transformere data fra databasen var selvfølgelig variert. 

Logikken var enten i kontrollerene eller i tjenesteklassene. 

Dette ser ut til å være mindre problemer, men de bremset utviklingen betydelig og reduserte kvaliteten, noe som førte til ustabilitet og feil. 

Kompleksiteten i en stor utbygging

Det oppsto vanskeligheter i selve utviklingen. Det var nødvendig å lage forskjellige blokker av systemet, og parallelt. Å tilpasse behovene til hver komponent i en enkelt kode ble stadig vanskeligere. Det var ikke lett å bli enige og glede alle komponentene samtidig. I tillegg kom begrensninger i teknologi, spesielt med hensyn til basen og frontend. Det var nødvendig å forlate jQuery mot rammeverk på høyt nivå, spesielt når det gjelder klienttjenester (nettsted).

I enkelte deler av systemet vil databaser som er mer egnet for dette kunne brukes.. For eksempel, senere hadde vi bruken av å flytte fra Redis til CosmosDB for å lagre en ordrekurv. 

Team og utviklere involvert i deres felt ønsket tydeligvis mer autonomi for tjenestene sine, både når det gjelder utvikling og utrulling. Slå sammen konflikter, frigjør problemer. Hvis dette problemet er ubetydelig for 5 utviklere, vil alt bli mer alvorlig med 10, og enda mer med den planlagte veksten. Og fremover skulle utviklingen av en mobilapplikasjon (det startet i 2017, og i 2018 var det stort fall). 

Ulike deler av systemet krevde ulike nivåer av stabilitet, men på grunn av den sterke tilkoblingen til systemet, kunne vi ikke tilby dette. En feil i utviklingen av en ny funksjon i adminpanelet kunne godt ha skjedd i aksept av en bestilling på siden, fordi koden er vanlig og gjenbrukbar, databasen og dataene er også de samme.

Det ville trolig vært mulig å unngå disse feilene og problemene innenfor rammen av en slik monolitisk-modulær arkitektur: lag en ansvarsfordeling, refaktorer både koden og databasen, skille lagene tydelig fra hverandre, overvåk kvaliteten hver dag. Men de valgte arkitektoniske løsningene og fokuset på rask utvidelse av systemets funksjonalitet førte til problemer med hensyn til stabilitet.

Hvordan Power of the Mind-bloggen satte kassaapparatene på restauranter

Hvis veksten av pizzerianettverket (og belastningen) fortsatte i samme tempo, ville fallet etter en stund være slik at systemet ikke ville stige. Vel illustrerer problemene vi begynte å møte innen 2015, her er en slik historie. 

I bloggen"Tankekraft” var en widget som viste data om inntekter for året for hele nettverket. Widgeten fikk tilgang til Dodo public API, som gir disse dataene. Denne statistikken er for øyeblikket tilgjengelig på http://dodopizzastory.com/. Widgeten ble vist på hver side og sendte forespørsler på en tidtaker hvert 20. sekund. Forespørselen gikk til api.dodopizza.ru og ba om:

  • antall pizzeriaer i nettverket;

  • totale nettverksinntekter siden begynnelsen av året;

  • inntekter for i dag.

Forespørselen om statistikk over inntekter gikk rett til databasen og begynte å etterspørre data om bestillinger, aggregere data i farten og gi ut beløpet. 

Kassepulter på restauranter gikk til samme bestillingsbord, lastet ut en liste over bestillinger mottatt for i dag, og nye bestillinger ble lagt til. Kasseapparater gjorde sine forespørsler hvert 5. sekund eller på sideoppdatering.

Diagrammet så slik ut:

Historien om Dodo IS-arkitekturen: En tidlig monolitt

En høst skrev Fjodor Ovchinnikov en lang og populær artikkel på bloggen sin. Mange kom til bloggen og begynte å lese alt nøye. Mens hver av personene som kom leste artikkelen, fungerte inntektsmodulen som den skal og ba om API hvert 20. sekund.

APIen kalte en lagret prosedyre for å beregne summen av alle bestillinger siden begynnelsen av året for alle pizzeriaer i nettverket. Aggregeringen var basert på ordretabellen, som er veldig populær. Alle kassene til alle åpne restauranter på den tiden går til den. Kasser sluttet å svare, bestillinger ble ikke akseptert. De ble heller ikke akseptert fra siden, dukket ikke opp på trackeren, skiftlederen kunne ikke se dem i grensesnittet sitt. 

Dette er ikke den eneste historien. Høsten 2015 var belastningen på systemet kritisk hver fredag. Flere ganger har vi slått av den offentlige API-en, og en gang måtte vi til og med slå av siden, fordi ingenting hjalp. Det var til og med en liste over tjenester med en avstengningsordre under tung belastning.

Fra nå av starter vår kamp med belastninger og for stabilisering av systemet (fra høsten 2015 til høsten 2018). Det var da det skjedde"flott høst". Videre oppstod det også noen ganger feil, noen var svært følsomme, men den generelle perioden med ustabilitet kan nå anses som bestått.

Rask virksomhetsvekst

Hvorfor kunne det ikke gjøres med en gang? Bare se på følgende grafer.

Historien om Dodo IS-arkitekturen: En tidlig monolitt

Også i 2014-2015 var det en åpning i Romania og en åpning i USA var under forberedelse.

Nettverket vokste veldig raskt, nye land ble åpnet, nye formater av pizzeriaer dukket opp, for eksempel ble det åpnet en pizzeria ved matretten. Alt dette krevde betydelig oppmerksomhet spesielt til utvidelsen av Dodo IS-funksjoner. Uten alle disse funksjonene, uten sporing på kjøkkenet, regnskap for produkter og tap i systemet, visning av utstedelse av en ordre i food court-hallen, ville vi neppe snakket om den "riktige" arkitekturen og den "riktige" tilnærmingen til utvikling nå.

En annen hindring for rettidig revisjon av arkitekturen og generelt oppmerksomhet på tekniske problemer var krisen i 2014. Ting som dette rammer hardt for muligheter for team til å vokse, spesielt for en ung bedrift som Dodo Pizza.

Raske løsninger som hjalp

Problemer trengte løsninger. Konvensjonelt kan løsninger deles inn i 2 grupper:

  • Raske som slukker brannen og gir en liten sikkerhetsmargin og kjøper oss tid til å endre.

  • Systemisk og derfor lang. Rekonstruksjon av en rekke moduler, oppdeling av en monolitisk arkitektur i separate tjenester (de fleste av dem er ikke i det hele tatt mikrotjenester, men snarere makrotjenester, og det er noe med det Andrey Morevskiys rapport). 

Den tørre listen over raske endringer er som følger:

Skaler opp basemaster

Selvfølgelig er det første som gjøres for å håndtere belastninger å øke kapasiteten til serveren. Dette ble gjort for hoveddatabasen og for webservere. Akk, dette er bare mulig opp til en viss grense, da blir det for dyrt.

Siden 2014 har vi flyttet til Azure, vi skrev også om dette emnet på den tiden i artikkelen "Hvordan Dodo Pizza leverer pizza ved hjelp av Microsoft Azure-skyen". Men etter en rekke økninger i serveren for basen, kom de opp mot kostnadene. 

Grunnreplikaer for lesing

To kopier ble laget for basen:

Les Replika for referanseforespørsler. Den brukes til å lese kataloger, type, by, gate, pizzeria, produkter (sakte endret domene), og i de grensesnittene der en liten forsinkelse er akseptabel. Det var 2 av disse kopiene, vi sikret tilgjengeligheten på samme måte som mesterne.

Les Replika for rapportforespørsler. Denne databasen hadde lavere tilgjengelighet, men alle rapporter gikk til den. La dem ha tunge forespørsler om enorme dataomberegninger, men de påvirker ikke hoveddatabasen og operasjonelle grensesnitt. 

Buffer i kode

Det var ingen cacher noe sted i koden (i det hele tatt). Dette førte til flere, ikke alltid nødvendige, forespørsler til den lastede databasen. Cacher var først både i minnet og på en ekstern cachetjeneste, det var Redis. Alt ble ugyldig av tid, innstillingene ble spesifisert i koden.

Flere backend-servere

Baksiden av applikasjonen måtte også skaleres for å håndtere de økte arbeidsbelastningene. Det var nødvendig å lage en klynge fra én iis-server. Vi har endret tidsplanen søknadsøkt fra minne til RedisCache, som gjorde det mulig å lage flere servere bak en enkel load balancer med round robin. Først ble den samme Redis brukt som til cacher, deretter ble den delt opp i flere. 

Som et resultat har arkitekturen blitt mer komplisert ...

Historien om Dodo IS-arkitekturen: En tidlig monolitt

… men noe av spenningen ble fjernet.

Og så var det nødvendig å gjøre om de lastede komponentene, som vi påtok oss. Vi vil snakke om dette i neste del.

Kilde: www.habr.com

Legg til en kommentar