Història de l'arquitectura Dodo IS: un monòlit primerenc

O cada empresa infeliç amb un monòlit és infeliç a la seva manera.

El desenvolupament del sistema Dodo IS va començar immediatament, com el negoci Dodo Pizza, el 2011. Es va basar en la idea de la digitalització completa i total dels processos de negoci, i per compte propi, que encara aleshores el 2011 va provocar moltes preguntes i escepticisme. Però des de fa 9 anys que seguim aquest camí, amb un desenvolupament propi, que va començar amb un monòlit.

Aquest article és una "resposta" a les preguntes "Per què reescriure l'arquitectura i fer canvis tan a gran escala i a llarg termini?" tornar a l'article anterior "Història de l'arquitectura Dodo IS: el camí del back office". Començaré per com va començar el desenvolupament de Dodo IS, com era l'arquitectura original, com van aparèixer els nous mòduls i per quins problemes s'havien de fer canvis a gran escala.

Història de l'arquitectura Dodo IS: un monòlit primerenc

Sèrie d'articles "Què és Dodo IS?" parla de:

  1. Monòlit primerenc a Dodo IS (2011-2015). (estàs aquí)

  2. El camí de back office: bases i bus separats.

  3. El camí lateral del client: façana sobre la base (2016-2017). (En progrés...)

  4. La història dels microserveis reals. (2018-2019). (En progrés...)

  5. Acabat de serrat del monòlit i estabilització de l'arquitectura. (En progrés…)

Arquitectura inicial

El 2011, l'arquitectura Dodo IS tenia aquest aspecte:

Història de l'arquitectura Dodo IS: un monòlit primerenc

El primer mòdul de l'arquitectura és l'acceptació de comandes. El procés de negoci va ser:

  • el client truca a la pizzeria;

  • el gerent agafa el telèfon;

  • accepta una comanda per telèfon;

  • l'omple paral·lelament a la interfície d'acceptació de comandes: es té en compte la informació sobre el client, les dades de la comanda, l'adreça de lliurament. 

La interfície del sistema d'informació semblava així...

Primera versió d'octubre de 2011:

Va millorar lleugerament el gener de 2012

Sistema d'informació de Dodo Pizza Restaurant Pizza de lliurament

Els recursos per al desenvolupament del mòdul de primera comanda eren limitats. Havíem de fer molt, ràpid i amb un equip petit. Un petit equip són 2 desenvolupadors que van establir les bases de tot el futur sistema.

La seva primera decisió va determinar el destí de la pila tecnològica:

  • Backend en ASP.NET MVC, llenguatge C#. Els desenvolupadors eren dotnetchiki, aquesta pila els era familiar i agradable.

  • Frontend a Bootstrap i JQuery: interfícies d'usuari sobre estils i scripts escrits per ells mateixos. 

  • Base de dades MySQL: sense costos de llicència, fàcil d'utilitzar.

  • Servidors a Windows Server, perquè llavors .NET només podria estar sota Windows (no parlarem de Mono).

Físicament, tot això s'expressava en el "dedic at the hoster". 

Arquitectura d'aplicacions de recepció de comandes

Llavors tothom ja parlava de microserveis, i SOA es va utilitzar en grans projectes durant 5 anys, per exemple, WCF es va llançar el 2006. Però després van triar una solució fiable i provada.

Aquí està.

Història de l'arquitectura Dodo IS: un monòlit primerenc

Asp.Net MVC és Razor, que, a petició d'un formulari o d'un client, representa una pàgina HTML amb renderització del servidor. Al client, els scripts CSS i JS ja mostren informació i, si cal, realitzen peticions AJAX mitjançant JQuery.

Les sol·licituds al servidor acaben a les classes *Controller, on el processament i la generació de la pàgina HTML final té lloc en el mètode. Els controladors fan peticions a una capa de lògica anomenada *Serveis. Cadascun dels serveis corresponia a algun aspecte del negoci:

  • Per exemple, DepartmentStructureService va donar informació sobre pizzeries, sobre departaments. Un departament és un grup de pizzeries dirigit per un sol franquiciat.

  • ReceivingOrdersService va acceptar i calcular la composició de la comanda.

  • I SmsService va enviar SMS trucant als serveis de l'API per enviar SMS.

Els serveis processen dades de la base de dades, emmagatzemen la lògica de negoci. Cada servei tenia un o més *Repositoris amb el nom adequat. Ja contenien consultes als procediments emmagatzemats a la base de dades i una capa de mapeadors. Hi havia lògica empresarial als emmagatzematges, especialment en aquells que emetien dades d'informes. No es va utilitzar ORM, tothom confiava en sql escrit a mà. 

També hi havia una capa del model de domini i classes auxiliars comunes, per exemple, la classe Order que emmagatzemava l'ordre. Al mateix lloc, a la capa, hi havia un ajudant per convertir el text visualitzat segons la moneda seleccionada.

Tot això es pot representar amb aquest model:

Història de l'arquitectura Dodo IS: un monòlit primerenc

Camí de la comanda

Penseu en una forma inicial simplificada de crear aquesta comanda.

Història de l'arquitectura Dodo IS: un monòlit primerenc

Inicialment, el lloc era estàtic. Hi havia preus i, a sobre, un número de telèfon i la inscripció "Si vols pizza, truca al número i fes la teva comanda". Per fer una comanda, hem d'implementar un flux senzill: 

  • El client visita un lloc estàtic amb preus, selecciona productes i truca al número que apareix al lloc.

  • El client anomena els productes que vol afegir a la comanda.

  • Dona la seva adreça i nom.

  • L'operador accepta la comanda.

  • La comanda es mostra a la interfície de comandes acceptades.

Tot comença amb la visualització del menú. Un usuari-operador connectat només accepta una comanda alhora. Per tant, el carro d'esborrany es pot emmagatzemar a la seva sessió (la sessió de l'usuari s'emmagatzema a la memòria). Hi ha un objecte Cart que conté productes i informació del client.

El client anomena el producte, l'operador fa clic + al costat del producte i s'envia una sol·licitud al servidor. La informació sobre el producte s'extreu de la base de dades i la informació sobre el producte s'afegeix al carretó.

Història de l'arquitectura Dodo IS: un monòlit primerenc

Nota. Sí, aquí no podeu treure el producte de la base de dades, sinó transferir-lo des de la interfície. Però per a més claredat, vaig mostrar exactament el camí des de la base de dades. 

A continuació, introduïu l'adreça i el nom del client. 

Història de l'arquitectura Dodo IS: un monòlit primerenc

Quan feu clic a "Crea una comanda":

  • La sol·licitud s'envia a OrderController.SaveOrder().

  • Obtenim el carretó de la sessió, hi ha productes en la quantitat que necessitem.

  • Complementem el Cart amb informació sobre el client i la passem al mètode AddOrder de la classe ReceivingOrderService, on es desa a la base de dades. 

  • La base de dades té taules amb la comanda, la composició de la comanda, el client, i totes estan connectades.

  • La interfície de visualització de comandes surt i treu les últimes comandes i les mostra.

Nous mòduls

Prendre l'ordre era important i necessari. No podeu fer un negoci de pizza si no teniu una comanda per vendre. Per tant, el sistema va començar a adquirir funcionalitat, aproximadament del 2012 al 2015. Durant aquest temps, van aparèixer molts blocs diferents del sistema, als quals anomenaré mòduls, en contraposició al concepte de servei o producte. 

Un mòdul és un conjunt de funcions que estan unides per un objectiu comercial comú. Al mateix temps, es troben físicament a la mateixa aplicació.

Els mòduls es poden anomenar blocs del sistema. Per exemple, aquest és un mòdul d'informes, interfícies d'administració, rastrejador d'aliments a la cuina, autorització. Totes aquestes són interfícies d'usuari diferents, algunes fins i tot tenen diferents estils visuals. Al mateix temps, tot està dins del marc d'una aplicació, un procés en execució. 

Tècnicament, els mòduls es van dissenyar com a Àrea (fins i tot es va mantenir una idea així nucli asp.net). Hi havia fitxers separats per a la interfície, models, així com les seves pròpies classes de controlador. Com a resultat, el sistema es va transformar d'aquest ...

Història de l'arquitectura Dodo IS: un monòlit primerenc

...en això:

Història de l'arquitectura Dodo IS: un monòlit primerenc

Alguns mòduls s'implementen per llocs separats (projecte executable), a causa d'una funcionalitat completament independent i en part a causa d'un desenvolupament lleugerament separat i més centrat. Això:

  • Planta - primera versió lloc dodopizza.ru.

  • Exporta: penjant informes de Dodo IS per a 1C. 

  • Personal - compte personal de l'empleat. Es va desenvolupar per separat i té el seu propi punt d'entrada i un disseny independent.

  • fs — un projecte per allotjar estàtica. Més tard ens n'hem allunyat, traslladant tota l'estàtica al CDN d'Akamai. 

La resta de blocs estaven a l'aplicació BackOffice. 

Història de l'arquitectura Dodo IS: un monòlit primerenc

Explicació del nom:

  • Caixer - Caixer del restaurant.

  • ShiftManager: interfícies per al rol de "Shift Manager": estadístiques operatives de vendes de pizzeria, la possibilitat de posar productes a la llista de parades, canviar l'ordre.

  • OfficeManager: interfícies per als rols de "Gestor de pizzeria" i "Franquiciat". Aquí es recullen les funcions per configurar una pizzeria, les seves promocions de bonificació, rebre i treballar amb els empleats, informes.

  • PublicScreens: interfícies per a televisors i tauletes penjades a les pizzeries. Els televisors mostren menús, informació publicitària, estat de la comanda a l'entrega. 

Van utilitzar una capa de servei comú, un bloc de classe de domini Dodo.Core comú i una base comuna. De vegades encara podrien conduir al llarg de les transicions entre ells. Incloent llocs individuals, com ara dodopizza.ru o personal.dodopizza.ru, van anar als serveis generals.

Quan van aparèixer nous mòduls, vam intentar reutilitzar al màxim el codi de serveis, procediments emmagatzemats i taules ja creats a la base de dades. 

Per a una millor comprensió de l'escala dels mòduls realitzats en el sistema, aquí teniu un diagrama de 2012 amb plans de desenvolupament:

Història de l'arquitectura Dodo IS: un monòlit primerenc

El 2015, tot estava al mapa i encara més en producció.

  • L'acceptació de la comanda s'ha convertit en un bloc separat del Contact Center, on l'operador accepta la comanda.

  • Hi havia pantalles públiques amb menús i informació penjada a les pizzeries.

  • La cuina disposa d'un mòdul que reprodueix automàticament el missatge de veu "New Pizza" quan arriba una nova comanda, i també imprimeix una factura per al missatger. Això simplifica molt els processos a la cuina, permet que els empleats no es distreguin amb un gran nombre d'operacions senzilles.

  • La unitat de lliurament es va convertir en una caixa de lliurament independent, on la comanda es va emetre al missatger que havia fet el torn anteriorment. El seu temps de treball es va tenir en compte per al càlcul de la nòmina. 

Paral·lelament, del 2012 al 2015, van aparèixer més de 10 desenvolupadors, van obrir 35 pizzeries, van desplegar el sistema a Romania i es van preparar per a l'obertura de punts de venda als Estats Units. Els desenvolupadors ja no es van ocupar de totes les tasques, sinó que es van dividir en equips. cadascun especialitzat en la seva part del sistema. 

Problemes

Incloent per l'arquitectura (però no només).

Caos a la base

Una base és convenient. S'hi pot aconseguir coherència, i a costa de les eines incorporades a les bases de dades relacionals. Treballar-hi és familiar i còmode, sobretot si hi ha poques taules i poques dades.

Però durant 4 anys de desenvolupament, la base de dades va resultar tenir unes 600 taules, 1500 procediments emmagatzemats, molts dels quals també tenien lògica. Per desgràcia, els procediments emmagatzemats no aporten gaire avantatge quan es treballa amb MySQL. La base no els guarda a la memòria cau i emmagatzemar-hi la lògica complica el desenvolupament i la depuració. La reutilització del codi també és difícil.

Moltes taules no tenien índexs adequats, en algun lloc, al contrari, hi havia molts índexs, que en dificultava la inserció. Va ser necessari modificar unes 20 taules: la transacció per crear una comanda podia trigar uns 3-5 segons. 

Les dades de les taules no sempre van ser de la forma més adequada. En algun lloc calia fer desnormalització. Part de les dades rebudes habitualment es trobaven en una columna en forma d'estructura XML, això augmentava el temps d'execució, allargava les consultes i complicava el desenvolupament.

A les mateixes taules es van produir molt peticions heterogènies. Les taules populars van patir especialment, com la taula esmentada anteriorment. ordres o taules pizzeria. S'utilitzaven per mostrar interfícies operatives a la cuina, analítiques. Un altre lloc els va contactar (dodopizza.ru), on en un moment donat podrien arribar de sobte moltes peticions. 

Les dades no es van agregar i molts càlculs es van fer sobre la marxa utilitzant la base. Això va crear càlculs innecessaris i càrrega addicional. 

Sovint, el codi anava a la base de dades quan no podia fer-ho. En algun lloc no hi havia prou operacions massives, en algun lloc caldria estendre una sol·licitud en diverses a través del codi per tal d'accelerar i augmentar la fiabilitat. 

Cohesió i ofuscació en el codi

Els mòduls que se suposa que havien de ser responsables de la seva part del negoci no ho van fer amb honestedat. Alguns d'ells tenien duplicació de funcions per rols. Per exemple, un venedor local que és responsable de l'activitat de màrqueting de la xarxa a la seva ciutat havia d'utilitzar tant la interfície "Administrador" (per crear promocions) com la interfície "Administrador d'oficina" (per veure l'impacte de les promocions en el negoci). Per descomptat, dins dels dos mòduls s'utilitzava el mateix servei que funcionava amb les promocions de bonificació.

Els serveis (classes dins d'un gran projecte monolític) podrien trucar-se entre ells per enriquir les seves dades.

Amb les classes model que emmagatzemen dades, el treball en el codi es va dur a terme de manera diferent. En algun lloc hi havia constructors a través dels quals era possible especificar camps obligatoris. En algun lloc això es va fer a través de propietats públiques. Per descomptat, obtenir i transformar dades de la base de dades era variat. 

La lògica estava en els controladors o en les classes de servei. 

Sembla que es tracta de problemes menors, però van frenar molt el desenvolupament i van reduir la qualitat, donant lloc a inestabilitat i errors. 

La complexitat d'un gran desenvolupament

Van sorgir dificultats en el propi desenvolupament. Calia fer diferents blocs del sistema, i en paral·lel. Encaixar les necessitats de cada component en un sol codi es va fer cada cop més difícil. No va ser fàcil posar-se d'acord i complaure tots els components alhora. A això s'hi van afegir limitacions tecnològiques, especialment pel que fa a la base i la interfície. Calia abandonar jQuery cap a frameworks d'alt nivell, sobretot pel que fa als serveis al client (lloc web).

En algunes parts del sistema, es podrien utilitzar bases de dades més adequades per a això.. Per exemple, més tard vam tenir el cas d'ús de passar de Redis a CosmosDB per emmagatzemar una cistella de comandes. 

Els equips i desenvolupadors implicats en el seu camp volien clarament més autonomia per als seus serveis, tant pel que fa al desenvolupament com al desplegament. Combina conflictes, allibera problemes. Si per a 5 desenvolupadors aquest problema és insignificant, amb 10, i encara més amb el creixement previst, tot es tornaria més greu. I per davant havia d'estar el desenvolupament d'una aplicació mòbil (va començar el 2017, i el 2018 va ser gran caiguda). 

Les diferents parts del sistema requerien diferents nivells d'estabilitat, però a causa de la forta connectivitat del sistema, no hem pogut proporcionar-ho. Es podria haver produït un error en el desenvolupament d'una nova funció al panell d'administració en l'acceptació d'una comanda al lloc, perquè el codi és comú i reutilitzable, la base de dades i les dades també són iguals.

Probablement seria possible evitar aquests errors i problemes en el marc d'una arquitectura tan monolítico-modular: fer una divisió de responsabilitat, refactoritzar tant el codi com la base de dades, separar clarament les capes entre si, controlar la qualitat cada dia. Però les solucions arquitectòniques escollides i el focus en la ràpida expansió de la funcionalitat del sistema van provocar problemes d'estabilitat.

Com el blog Power of the Mind posa les caixes registradores als restaurants

Si el creixement de la xarxa de pizzeries (i la càrrega) continués al mateix ritme, després d'un temps les caigudes serien tals que el sistema no augmentaria. Il·lustra bé els problemes que vam començar a afrontar el 2015, aquí teniu una història així. 

Al blog "Poder mental” era un giny que mostrava dades sobre els ingressos de l'any de tota la xarxa. El giny va accedir a l'API pública de Dodo, que proporciona aquestes dades. Aquesta estadística està disponible actualment a http://dodopizzastory.com/. El giny es mostrava a cada pàgina i feia sol·licituds amb un temporitzador cada 20 segons. La sol·licitud va anar a api.dodopizza.ru i va demanar:

  • el nombre de pizzeries a la xarxa;

  • els ingressos totals de la xarxa des de principis d'any;

  • ingressos per avui.

La sol·licitud d'estadístiques d'ingressos va anar directament a la base de dades i va començar a sol·licitar dades sobre comandes, agregar dades sobre la marxa i donar l'import. 

Els taulells de caixa dels restaurants van anar a la mateixa taula de comandes, van descarregar una llista de comandes rebudes per avui i s'hi van afegir noves comandes. Les caixes registradores feien les seves sol·licituds cada 5 segons o en l'actualització de la pàgina.

El diagrama tenia aquest aspecte:

Història de l'arquitectura Dodo IS: un monòlit primerenc

Una tardor, Fiódor Ovtxinnikov va escriure un article llarg i popular al seu bloc. Molta gent va venir al blog i va començar a llegir-ho tot amb atenció. Mentre cadascuna de les persones que van venir estava llegint l'article, el giny d'ingressos funcionava correctament i sol·licitava l'API cada 20 segons.

L'API va cridar un procediment emmagatzemat per calcular la suma de totes les comandes des de principis d'any per a totes les pizzeries de la cadena. L'agregació es va basar en la taula de comandes, que és molt popular. Hi acudeixen tots els taulells de caixa de tots els restaurants oberts en aquell moment. Els taulells de caixa van deixar de respondre, no es van acceptar comandes. Tampoc van ser acceptats del lloc, no van aparèixer al rastrejador, el cap de torn no els va poder veure a la seva interfície. 

Aquesta no és l'única història. A la tardor del 2015, cada divendres la càrrega del sistema era crítica. Diverses vegades vam desactivar l'API pública, i una vegada, fins i tot vam haver de desactivar el lloc, perquè res no va ajudar. Fins i tot hi havia una llista de serveis amb una ordre d'aturada sota càrregues pesades.

A partir d'ara comença la nostra lluita amb les càrregues i per l'estabilització del sistema (de la tardor de 2015 a la tardor de 2018). Va ser llavors quan va passar"gran caiguda". A més, de vegades també es van produir falles, algunes eren molt sensibles, però el període general d'inestabilitat ara es pot considerar passat.

Ràpid creixement empresarial

Per què no es va poder fer de seguida? Només cal mirar els gràfics següents.

Història de l'arquitectura Dodo IS: un monòlit primerenc

També el 2014-2015 hi va haver una obertura a Romania i s'estava preparant una obertura als EUA.

La xarxa va créixer molt ràpidament, es van obrir nous països, van aparèixer nous formats de pizzeries, per exemple, es va obrir una pizzeria a la zona de restauració. Tot això va requerir una atenció important específicament a l'expansió de les funcions de Dodo IS. Sense totes aquestes funcions, sense seguiment a la cuina, comptabilització de productes i pèrdues en el sistema, visualització de l'emissió d'una ordre a la sala de menjador, difícilment estaríem parlant de l'arquitectura "correcta" i l'enfocament "correcte" de desenvolupament ara.

Un altre obstacle per a la revisió oportuna de l'arquitectura i l'atenció general als problemes tècnics va ser la crisi del 2014. Coses com aquesta afectaven molt les oportunitats de creixement dels equips, especialment per a una empresa jove com Dodo Pizza.

Solucions ràpides que han ajudat

Els problemes necessiten solucions. Convencionalment, les solucions es poden dividir en 2 grups:

  • Uns ràpids que apaguen el foc i donen un petit marge de seguretat i ens donen temps per canviar.

  • Sistèmic i, per tant, llarg. Reenginyeria d'una sèrie de mòduls, divisió d'una arquitectura monolítica en serveis separats (la majoria d'ells no són gens micro, sinó macroserveis, i hi ha alguna cosa al respecte Informe d'Andrey Morevskiy). 

La llista seca de canvis ràpids és la següent:

Ampliar el mestre base

Per descomptat, el primer que es fa per fer front a les càrregues és augmentar la capacitat del servidor. Això es va fer per a la base de dades mestra i per als servidors web. Per desgràcia, això només és possible fins a un cert límit, llavors es fa massa car.

Des del 2014, ens vam traslladar a Azure, també vam escriure sobre aquest tema en aquell moment a l'article "Com Dodo Pizza ofereix pizza mitjançant el Microsoft Azure Cloud". Però després d'una sèrie d'augments en el servidor de la base, es van enfrontar amb el cost. 

Rèpliques base per a la lectura

Es van fer dues rèpliques per a la base:

Llegir rèplica per a sol·licituds de referència. S'utilitza per llegir directoris, tipus, ciutat, carrer, pizzeria, productes (domini canviat lentament) i en aquelles interfícies on un petit retard és acceptable. N'hi havia 2 d'aquestes rèpliques, vam assegurar la seva disponibilitat de la mateixa manera que els mestres.

ReadReplica per a sol·licituds d'informe. Aquesta base de dades tenia una disponibilitat inferior, però tots els informes hi anaven. Deixeu que tinguin grans sol·licituds per a grans càlculs de dades, però no afecten la base de dades principal i les interfícies operatives. 

Cachés al codi

No hi havia memòria cau en cap lloc del codi (en absolut). Això va provocar sol·licituds addicionals, no sempre necessàries, a la base de dades carregada. Els cachés eren primer tant a la memòria com en un servei de memòria cau extern, que era Redis. Tot va ser invalidat pel temps, la configuració es va especificar al codi.

Múltiples servidors backend

El backend de l'aplicació també s'havia d'escalar per gestionar l'augment de les càrregues de treball. Calia fer un clúster des d'un servidor iis. Hem reprogramat sessió d'aplicació de la memòria a RedisCache, que va permetre crear diversos servidors darrere d'un simple equilibrador de càrrega amb round robin. Al principi, s'utilitzava el mateix Redis que per a cachés, després es va dividir en diversos. 

Com a resultat, l'arquitectura s'ha tornat més complicada...

Història de l'arquitectura Dodo IS: un monòlit primerenc

… però part de la tensió es va eliminar.

I després va ser necessari refer els components carregats, cosa que vam assumir. D'això en parlarem a la següent part.

Font: www.habr.com

Afegeix comentari