Historien om Dodo IS-arkitekturen: En tidlig monolit

Eller enhver ulykkelig virksomhed med en monolit er ulykkelig på sin egen måde.

Udviklingen af ​​Dodo IS-systemet begyndte straks, ligesom Dodo Pizza-forretningen, i 2011. Det var baseret på ideen om fuldstændig og total digitalisering af forretningsprocesser, og alene, hvilket allerede dengang i 2011 vakte en masse spørgsmål og skepsis. Men i 9 år nu har vi fulgt denne vej - med vores egen udvikling, som begyndte med en monolit.

Denne artikel er et "svar" på spørgsmålene "Hvorfor omskrive arkitekturen og foretage sådanne store og langsigtede ændringer?" tilbage til forrige artikel "History of the Dodo IS Architecture: The Way of the Back Office". Jeg starter med, hvordan udviklingen af ​​Dodo IS begyndte, hvordan den oprindelige arkitektur så ud, hvordan nye moduler dukkede op, og på grund af hvilke problemer der måtte foretages store ændringer.

Historien om Dodo IS-arkitekturen: En tidlig monolit

Artikelserie "Hvad er Dodo IS?" fortæller om:

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

  2. Back Office-stien: Separate baser og bus.

  3. Bygherrens sidesti: facade over basen (2016-2017). (I gang...)

  4. Historien om rigtige mikrotjenester. (2018-2019). (I gang...)

  5. Færdig savning af monolitten og stabilisering af arkitekturen. (I gang...)

Oprindelig arkitektur

I 2011 så Dodo IS-arkitekturen sådan ud:

Historien om Dodo IS-arkitekturen: En tidlig monolit

Det første modul i arkitekturen er ordreaccept. Forretningsprocessen var:

  • kunden ringer til pizzeriaet;

  • lederen tager telefonen;

  • accepterer en ordre via telefon;

  • udfylder det parallelt i ordreacceptgrænsefladen: det tager hensyn til oplysninger om kunden, data om ordredetaljer, leveringsadresse. 

Grænsefladen af ​​informationssystemet så nogenlunde sådan her ud ...

Første version fra oktober 2011:

Lidt forbedret i januar 2012

Dodo Pizza Information System Levering Pizza Restaurant

Ressourcerne til udviklingen af ​​det første ordremodtagende modul var begrænsede. Vi skulle gøre meget, hurtigt og med et lille hold. Et lille team er 2 udviklere, som har lagt grunden til hele det fremtidige system.

Deres første beslutning afgjorde teknologistakkens skæbne:

  • Backend på ASP.NET MVC, C# sprog. Udviklerne var dotnetchiki, denne stak var velkendt og behagelig for dem.

  • Frontend på Bootstrap og JQuery: brugergrænseflader på selvskrevne stilarter og scripts. 

  • MySQL-database: ingen licensomkostninger, nem at bruge.

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

Fysisk kom alt dette til udtryk i "dedicen hos værten". 

Ordreindtag applikationsarkitektur

Da talte alle allerede om mikrotjenester, og SOA blev brugt i store projekter i 5 år, for eksempel blev WCF udgivet i 2006. Men så valgte de en pålidelig og gennemprøvet løsning.

Her er det.

Historien om Dodo IS-arkitekturen: En tidlig monolit

Asp.Net MVC er Razor, som efter anmodning fra en formular eller fra en klient gengiver en HTML-side med servergengivelse. På klienten viser CSS- og JS-scripts allerede information og udfører om nødvendigt AJAX-anmodninger gennem JQuery.

Forespørgsler på serveren ender i *Controller-klasserne, hvor behandlingen og genereringen af ​​den endelige HTML-side foregår i metoden. Controllere sender anmodninger til et logiklag kaldet *Services. Hver af tjenesterne svarede til nogle aspekter af virksomheden:

  • For eksempel udgav DepartmentStructureService information om pizzeriaer, om afdelinger. En afdeling er en gruppe pizzeriaer, der drives af en enkelt franchisetager.

  • ReceivingOrdersService accepterede og beregnede ordresammensætningen.

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

Tjenester behandlede data fra databasen, lagret forretningslogik. Hver tjeneste havde et eller flere *Repositories med det passende navn. De indeholdt allerede forespørgsler til lagrede procedurer i databasen og et lag af kortlæggere. Der var forretningslogik i lagrene, især meget i dem, der udsendte rapporteringsdata. ORM blev ikke brugt, alle stolede på håndskrevet sql. 

Der var også et lag af domænemodellen og fælles hjælperklasser, for eksempel Order-klassen, der lagrede ordren. Samme sted, i laget, var der en hjælper til at konvertere displayteksten efter den valgte valuta.

Alt dette kan repræsenteres af en sådan model:

Historien om Dodo IS-arkitekturen: En tidlig monolit

Bestil måde

Overvej en forenklet indledende måde at oprette en sådan ordre på.

Historien om Dodo IS-arkitekturen: En tidlig monolit

I starten var siden statisk. Den havde priser på, og oveni - et telefonnummer og påskriften "Hvis du vil have pizza - ring til nummeret og bestil." For at bestille skal vi implementere et simpelt flow: 

  • Kunden besøger et statisk websted med priser, vælger produkter og ringer til nummeret, der er anført på webstedet.

  • Kunden navngiver de produkter, de ønsker at tilføje til ordren.

  • Oplyser hans adresse og navn.

  • Operatøren accepterer ordren.

  • Ordren vises i den accepterede ordregrænseflade.

Det hele starter med at vise menuen. En logget ind bruger-operatør accepterer kun én ordre ad gangen. Derfor kan kladdevognen gemmes i hans session (brugerens session gemmes i hukommelsen). Der er et vognobjekt, der indeholder produkter og information om kunden.

Kunden navngiver produktet, operatøren klikker på + ved siden af ​​produktet, og en anmodning sendes til serveren. Oplysninger om produktet trækkes ud af databasen, og oplysninger om produktet lægges i indkøbskurven.

Historien om Dodo IS-arkitekturen: En tidlig monolit

Bemærk. Ja, her kan du ikke trække produktet fra databasen, men overføre det fra frontend. Men for klarhedens skyld viste jeg præcis stien fra databasen. 

Indtast derefter adressen og navnet på klienten. 

Historien om Dodo IS-arkitekturen: En tidlig monolit

Når du klikker på "Opret ordre":

  • Anmodningen sendes til OrderController.SaveOrder().

  • Vi får Cart fra sessionen, der er produkter i den mængde vi skal bruge.

  • Vi supplerer indkøbskurven med information om klienten og videregiver den til AddOrder-metoden i ReceivingOrderService-klassen, hvor den gemmes i databasen. 

  • Databasen har tabeller med ordren, sammensætningen af ​​ordren, klienten, og de er alle forbundet.

  • Ordrevisningsgrænsefladen går og trækker de seneste ordrer ud og viser dem.

Nye moduler

Det var vigtigt og nødvendigt at tage imod ordren. Du kan ikke lave en pizzaforretning, hvis du ikke har en ordre at sælge. Derfor begyndte systemet at erhverve funktionalitet - cirka fra 2012 til 2015. I løbet af denne tid dukkede mange forskellige blokke af systemet op, som jeg vil kalde moduler, i modsætning til begrebet service eller produkt. 

Et modul er et sæt funktioner, der er forenet af et fælles forretningsmål. Samtidig er de fysisk i samme applikation.

Moduler kan kaldes systemblokke. For eksempel er dette et rapporteringsmodul, admin-grænseflader, madtracker i køkkenet, autorisation. Disse er alle forskellige brugergrænseflader, nogle har endda forskellige visuelle stilarter. Samtidig er alt inden for rammerne af én applikation, én kørende proces. 

Teknisk set var modulerne designet som Area (en sådan idé forblev endda i asp.net kerne). Der var separate filer til frontend, modeller såvel som deres egne controllerklasser. Som et resultat blev systemet transformeret fra denne ...

Historien om Dodo IS-arkitekturen: En tidlig monolit

...ind i dette:

Historien om Dodo IS-arkitekturen: En tidlig monolit

Nogle moduler er implementeret af separate sites (eksekverbart projekt), på grund af en helt separat funktionalitet og dels på grund af en lidt separat, mere fokuseret udvikling. Det her:

  • Webstedetførste version websted dodopizza.ru.

  • eksport: upload af rapporter fra Dodo IS for 1C. 

  • Personligt - medarbejderens personlige konto. Den blev udviklet separat og har sit eget indgangspunkt og separat design.

  • fs — et projekt til hosting af statik. Senere flyttede vi væk fra det og flyttede al statikken til Akamai CDN. 

Resten af ​​blokkene var i BackOffice-applikationen. 

Historien om Dodo IS-arkitekturen: En tidlig monolit

Navneforklaring:

  • Kasserer - Restaurantkasserer.

  • ShiftManager - grænseflader til "Shift Manager"-rollen: driftsstatistik over pizzeriasalg, muligheden for at sætte produkter på stoplisten, ændre rækkefølgen.

  • OfficeManager - grænseflader til rollerne "Pizzeria Manager" og "Franchisetager". Her er samlet funktioner til opsætning af et pizzeria, dets bonuskampagner, modtagelse og arbejde med medarbejdere, rapporter.

  • PublicScreens - grænseflader til tv og tablets hængende i pizzeriaer. TV'er viser menuer, reklameinformation, ordrestatus ved levering. 

De brugte et fælles servicelag, en fælles Dodo.Core domæneklasseblok og en fælles base. Nogle gange kunne de stadig føre langs overgangene til hinanden. Inklusive individuelle websteder, såsom dodopizza.ru eller personal.dodopizza.ru, gik til generelle tjenester.

Da nye moduler dukkede op, forsøgte vi at genbruge den allerede oprettede kode for tjenester, lagrede procedurer og tabeller i databasen maksimalt. 

For en bedre forståelse af skalaen af ​​de moduler, der er lavet i systemet, er her et diagram fra 2012 med udviklingsplaner:

Historien om Dodo IS-arkitekturen: En tidlig monolit

I 2015 var alt på kortet, og endnu mere var i produktion.

  • Ordreaccept er vokset til en separat blok af Contact Center, hvor ordren accepteres af operatøren.

  • Der var offentlige skærme med menuer og information hængende i pizzeriaer.

  • Køkkenet har et modul, der automatisk afspiller talebeskeden "Ny Pizza", når der kommer en ny ordre, og desuden udskriver en faktura til kureren. Dette forenkler i høj grad processerne i køkkenet, gør det muligt for medarbejderne ikke at blive distraheret af et stort antal simple operationer.

  • Leveringsenheden blev til en separat Delivery Checkout, hvor ordren blev udstedt til den kurer, der tidligere havde taget skiftet. Hans arbejdstid blev taget i betragtning ved lønberegningen. 

Sideløbende fra 2012 til 2015 dukkede mere end 10 udviklere op, 35 pizzeriaer åbnede, implementerede systemet til Rumænien og forberedte åbningen af ​​forretninger i USA. Udviklerne klarede ikke længere alle opgaverne, men var opdelt i teams. hver specialiseret sig i sin egen del af systemet. 

Problemer

Herunder på grund af arkitekturen (men ikke kun).

Kaos i basen

En base er praktisk. Konsistens kan opnås i det, og på bekostning af værktøjer indbygget i relationelle databaser. At arbejde med det er velkendt og bekvemt, især hvis der er få tabeller og få data.

Men over 4 års udvikling viste databasen sig at have omkring 600 tabeller, 1500 lagrede procedurer, hvoraf mange også havde logik. Ak, lagrede procedurer giver ikke den store fordel, når du arbejder med MySQL. De cachelagres ikke af basen, og lagring af logik i dem komplicerer udvikling og fejlfinding. Genbrug af kode er også svært.

Mange tabeller havde ikke passende indekser, et eller andet sted var der tværtimod en masse indekser, som gjorde det svært at indsætte. Det var nødvendigt at ændre omkring 20 tabeller - transaktionen for at oprette en ordre kunne tage omkring 3-5 sekunder. 

Dataene i tabellerne var ikke altid i den mest passende form. Et eller andet sted var det nødvendigt at lave denormalisering. En del af de regelmæssigt modtagne data var i en kolonne i form af en XML-struktur, hvilket øgede eksekveringstiden, forlængede forespørgslerne og komplicerede udviklingen.

Til de samme borde blev produceret meget heterogene anmodninger. Populære borde led især, ligesom tabellen nævnt ovenfor. ordrer eller borde pizzeria. De blev brugt til at vise operationelle grænseflader i køkkenet, analyser. Et andet websted kontaktede dem (dodopizza.ru), hvor der til enhver tid pludselig kunne komme en masse forespørgsler. 

Dataene blev ikke aggregeret og mange beregninger fandt sted i farten ved hjælp af basen. Dette skabte unødvendige beregninger og ekstra belastning. 

Ofte gik koden til databasen, når den ikke kunne have gjort det. Et eller andet sted var der ikke nok bulk operationer, et eller andet sted ville det være nødvendigt at sprede én anmodning i flere gennem koden for at fremskynde og øge pålideligheden. 

Sammenhæng og sløring i kode

Moduler, der skulle stå for deres del af forretningen, gjorde det ikke ærligt. Nogle af dem havde duplikering af funktioner til roller. For eksempel skulle en lokal marketingmedarbejder, der er ansvarlig for netværkets markedsføringsaktivitet i sin by, bruge både "Admin"-grænsefladen (for at oprette kampagner) og grænsefladen "Office Manager" (for at se kampagnernes indvirkning på virksomheden). Selvfølgelig brugte begge moduler den samme service, der arbejdede med bonuskampagner.

Tjenester (klasser inden for et monolitisk stort projekt) kunne ringe til hinanden for at berige deres data.

Med selve modelklasserne, der lagrer data, arbejdet i koden blev udført anderledes. Et eller andet sted var der konstruktører, hvorigennem det var muligt at angive påkrævede felter. Et eller andet sted blev dette gjort gennem offentlige ejendomme. Selvfølgelig var det varieret at hente og transformere data fra databasen. 

Logikken var enten i controllerne eller i serviceklasserne. 

Det ser ud til at være mindre problemer, men de bremsede udviklingen kraftigt og reducerede kvaliteten, hvilket førte til ustabilitet og fejl. 

Kompleksiteten af ​​en stor udvikling

Der opstod vanskeligheder i selve udviklingen. Det var nødvendigt at lave forskellige blokke af systemet, og parallelt. At tilpasse behovene for hver komponent i en enkelt kode blev stadig vanskeligere. Det var ikke nemt at blive enige og glæde alle komponenterne på samme tid. Hertil kom begrænsninger i teknologi, især med hensyn til basen og frontend. Det var nødvendigt at opgive jQuery mod rammer på højt niveau, især med hensyn til kundeservice (hjemmeside).

I nogle dele af systemet kunne der anvendes databaser, der er mere egnede til dette.. For eksempel havde vi senere brug for at flytte fra Redis til CosmosDB for at opbevare en ordrekurv. 

Teams og udviklere involveret i deres felt ønskede tydeligvis mere autonomi for deres tjenester, både med hensyn til udvikling og udrulning. Slå konflikter sammen, frigør problemer. Hvis dette problem for 5 udviklere er ubetydeligt, så med 10, og endnu mere med den planlagte vækst, ville alt blive mere alvorligt. Og forude skulle udviklingen af ​​en mobilapplikation (det startede i 2017, og i 2018 var det stort fald). 

Forskellige dele af systemet krævede forskellige niveauer af stabilitet, men på grund af systemets stærke forbindelse kunne vi ikke levere dette. En fejl i udviklingen af ​​en ny funktion i admin panelet kunne godt have fundet sted i accepten af ​​en ordre på siden, fordi koden er fælles og genbrugelig, databasen og data er også de samme.

Det ville formentlig være muligt at undgå disse fejl og problemer inden for rammerne af sådan en monolitisk-modulær arkitektur: Lav en ansvarsfordeling, refaktorer både koden og databasen, adskille lagene klart fra hinanden, overvåg kvaliteten hver dag. Men de valgte arkitektoniske løsninger og fokus på den hurtige udvidelse af systemets funktionalitet førte til problemer i forhold til stabilitet.

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

Hvis væksten af ​​pizzerianetværket (og belastningen) fortsatte i samme tempo, ville faldene efter et stykke tid være sådan, at systemet ikke ville stige. Godt illustrerer de problemer, vi begyndte at stå over for i 2015, her er sådan en historie. 

I bloggen"Sind magt” var en widget, der viste data om omsætning for året for hele netværket. Widgetten fik adgang til Dodo public API, som leverer disse data. Denne statistik er i øjeblikket tilgængelig på http://dodopizzastory.com/. Widget'en blev vist på hver side og stillede anmodninger på en timer hvert 20. sekund. Anmodningen gik til api.dodopizza.ru og anmodede om:

  • antallet af pizzeriaer i netværket;

  • samlede netværksindtægter siden begyndelsen af ​​året;

  • omsætning for i dag.

Forespørgslen om statistik over indtægter gik direkte til databasen og begyndte at anmode om data om ordrer, aggregere data på fluen og udlevere beløbet. 

Kasseskranker i restauranter gik til den samme ordrebord, lossede en liste over ordrer modtaget for i dag, og nye ordrer blev tilføjet til den. Kasseapparater lavede deres anmodninger hvert 5. sekund eller på sideopdatering.

Diagrammet så således ud:

Historien om Dodo IS-arkitekturen: En tidlig monolit

Et efterår skrev Fjodor Ovchinnikov en lang og populær artikel på sin blog. En masse mennesker kom til bloggen og begyndte at læse alt omhyggeligt. Mens hver af de personer, der kom, læste artiklen, fungerede indtægtswidgetten korrekt og anmodede om API'en hvert 20. sekund.

API'et kaldte en lagret procedure til at beregne summen af ​​alle ordrer siden begyndelsen af ​​året for alle pizzeriaer i netværket. Sammenlægningen var baseret på ordretabellen, som er meget populær. Alle kasseskranker i alle åbne restauranter på det tidspunkt går til den. Kasseskranker holdt op med at reagere, ordrer blev ikke accepteret. De blev heller ikke accepteret fra siden, dukkede ikke op på trackeren, vagtlederen kunne ikke se dem i sin grænseflade. 

Dette er ikke den eneste historie. I efteråret 2015 var belastningen på systemet hver fredag ​​kritisk. Flere gange har vi slået den offentlige API fra, og en gang måtte vi endda slukke for siden, for intet hjalp. Der var endda en liste over tjenester med en nedlukningsordre under tunge belastninger.

Fra nu af begynder vores kamp med belastninger og for stabilisering af systemet (fra efteråret 2015 til efteråret 2018). Det var da det skete"stort fald". Yderligere opstod der også nogle gange fejl, nogle var meget følsomme, men den generelle periode med ustabilitet kan nu betragtes som bestået.

Hurtig forretningsvækst

Hvorfor kunne det ikke gøres med det samme? Bare se på følgende diagrammer.

Historien om Dodo IS-arkitekturen: En tidlig monolit

Også i 2014-2015 var der en åbning i Rumænien, og en åbning i USA var under forberedelse.

Netværket voksede meget hurtigt, nye lande blev åbnet, nye formater af pizzeriaer dukkede op, for eksempel blev der åbnet et pizzeria ved food courten. Alt dette krævede betydelig opmærksomhed specifikt til udvidelsen af ​​Dodo IS-funktioner. Uden alle disse funktioner, uden sporing i køkkenet, regnskab for produkter og tab i systemet, visning af udstedelse af en ordre i food court-hallen, ville vi næppe tale om den "korrekte" arkitektur og den "korrekte" tilgang til udvikling nu.

En anden hindring for rettidig revision af arkitekturen og generelt opmærksomhed på tekniske problemer var krisen i 2014. Ting som dette rammer hårdt på mulighederne for teams til at vokse, især for en ung virksomhed som Dodo Pizza.

Hurtige løsninger, der hjalp

Problemer krævede løsninger. Konventionelt kan løsninger opdeles i 2 grupper:

  • Hurtige, der slukker ilden og giver en lille sikkerhedsmargin og giver os tid til at ændre os.

  • Systemisk og derfor lang. Omstrukturering af et antal moduler, opdeling af en monolitisk arkitektur i separate tjenester (de fleste af dem er slet ikke mikrotjenester, men snarere makrotjenester, og der er noget om det Andrey Morevskiys rapport). 

Den tørre liste over hurtige ændringer er som følger:

Opskaler basemaster

Selvfølgelig er det første, der gøres for at håndtere belastninger, at øge serverens kapacitet. Dette blev gjort for masterdatabasen og for webservere. Ak, dette er kun muligt op til en vis grænse, så bliver det for dyrt.

Siden 2014 er vi flyttet til Azure, vi skrev også om dette emne på det tidspunkt i artiklen "Hvordan Dodo Pizza leverer pizza ved hjælp af Microsoft Azure Cloud". Men efter en række stigninger i serveren til basen kom de op imod omkostningerne. 

Grundreplikaer til læsning

To replikaer blev lavet til basen:

Læs Replika for referenceanmodninger. Det bruges til at læse mapper, type, by, gade, pizzeria, produkter (langsomt ændret domæne) og i de grænseflader, hvor en lille forsinkelse er acceptabel. Der var 2 af disse replikaer, vi sikrede deres tilgængelighed på samme måde som masterne.

Læs Replika for rapportanmodninger. Denne database havde lavere tilgængelighed, men alle rapporter gik til den. Lad dem have tunge anmodninger om enorme datagenberegninger, men de påvirker ikke hoveddatabasen og operationelle grænseflader. 

Caches i kode

Der var ingen caches nogen steder i koden (overhovedet). Dette førte til yderligere, ikke altid nødvendige, anmodninger til den indlæste database. Caches var først både i hukommelsen og på en ekstern cachetjeneste, det var Redis. Alt blev ugyldigt af tid, indstillingerne blev angivet i koden.

Flere backend-servere

Backend af applikationen skulle også skaleres for at håndtere de øgede arbejdsbyrder. Det var nødvendigt at lave en klynge fra én iis-server. Vi har omlagt ansøgningssession fra memory til RedisCache, hvilket gjorde det muligt at lave flere servere bag en simpel load balancer med round robin. Først blev den samme Redis brugt som til caches, derefter blev den delt op i flere. 

Som et resultat er arkitekturen blevet mere kompliceret ...

Historien om Dodo IS-arkitekturen: En tidlig monolit

… men noget af spændingen blev fjernet.

Og så var det nødvendigt at lave de indlæste komponenter om, hvilket vi påtog os. Vi vil tale om dette i næste del.

Kilde: www.habr.com

Tilføj en kommentar