Хабр мяняе свет. Больш за год мы вядзем свой блог. Недзе паўгода таму нам прыляцеў цалкам лагічны фідбэк ад хабраўчан: «Дода, вось вы ўсюды кажаце, што ў вас свая сістэма. А што гэта за сыстэма? І навошта яна патрэбна сетцы піцэрый?».
Мы пасядзелі, падумалі і зразумелі, што вы маеце рацыю. Мы спрабуем растлумачыць усё на пальцах, але выходзіць ірванымі кавалкамі і нідзе няма паўнавартаснага апісання сістэмы. Так пачаўся доўгі шлях збору інфармацыі, пошуку аўтараў і напісанні серыі артыкулаў пра Dodo IS. Пагналі!
Падзякі: дзякуй, што дзяліцеся сваім фідбэкам з намі. Дзякуючы яму мы нарэшце апісалі сістэму, склалі тэхнарадар і хутка выкацім вялікае апісанне нашых працэсаў. Без вас так бы і сядзелі яшчэ 5 год.
Серыя артыкулаў "Што такое Dodo IS?" раскажа пра:
Ранні маналіт у Dodo IS (2011-2015 гады). (In progress…)
Шлях бэкофіса: паасобныя базы і шына. (You are here)
Шлях кліенцкай часткі: фасад над базай (2016-2017 гады). (In progress…)
Гісторыя сапраўдных мікрасэрвісаў. (2018-2019 гады). (In progress…)
Скончаны распілоўванне маналіта і стабілізацыя архітэктуры. (In progress…)
Калі цікава даведацца нешта яшчэ - пішыце ў каментарах.
Меркаванне па храналагічным апісанні ад аўтара
Я рэгулярна праводжу сустрэчу для новых супрацоўнікаў па тэме "Архітэктура сістэмы". У нас яна называецца "Intro to Dodo IS Architecture" і з'яўляецца часткай працэсу онбордінг новых распрацоўшчыкаў. Распавядаючы ў тым ці іншым выглядзе пра нашу архітэктуру, пра яе асаблівасці, у мяне нарадзіўся некаторы гістарычны падыход да апісання.
Традыцыйна мы глядзім на сістэму, як на набор кампанентаў (тэхнічных або больш высокаўзроўневых), бізнесавых модуляў, якія ўзаемадзейнічаюць паміж сабой дзеля дасягнення якой-небудзь мэты. І калі для праектавання такі погляд апраўданы, то для апісання і разумення не зусім падыходзіць. Прычын тут некалькі:
Рэальнасць адрозніваецца ад таго, што на паперы. Не ўсё з задуманага атрымоўваецца. А нам цікава, як насамрэч усё аказалася і працуе.
Паслядоўнае выкладанне інфармацыі. Па сутнасці можна прайсціся храналагічна ад пачатку да бягучага стану.
Ад простага да складанага. Не ўніверсальна, але ў нашым выпадку менавіта так. Ад прасцейшых падыходаў архітэктура пераходзіла да больш складаных. Часта праз ускладненне вырашаліся праблемы хуткасці рэалізацыі і стабільнасці, а таксама дзясяткі іншых уласцівасцяў з спісу нефункцыянальных патрабаванняў.вось тут добра расказана пра супрацьпастаўленне складанасці астатнім патрабаванням).
У 2011 годзе архітэктура Dodo IS выглядала так:
Да 2020 года яна крыху ўскладнілася і стала такой:
Як адбылася гэтая эвалюцыя? Навошта патрэбны розныя часткі сістэмы? Якія архітэктурныя рашэнні і чаму былі прыняты? Разбярэмся ў гэтай серыі артыкулаў.
Першыя праблемы 2016 года: навошта сэрвісам выходзіць з маналіта
Першыя артыкулы з цыклу будуць пра сервісы, якія першымі аддзяліліся ад маналіта. Каб увесці вас у кантэкст, раскажу, якія праблемы былі ў нас у сістэме да пачатку 2016 года, што нам давялося займацца падзелам сэрвісаў.
Адзіная база MySql, у якую пісалі свае запісы ўсе прыкладанні, якія існавалі на той момант у Dodo IS. Следствы былі такія:
Вялікая нагрузка (пры гэтым 85 працэнтаў запытаў прыпадала на чытанне).
База разрасталася. З-за гэтага яе кошт і падтрымка станавіліся праблемай.
Адзіная кропка адмовы. Калі адно прыкладанне, якое піша ў базу, раптам пачынала рабіць гэта больш актыўна, то іншыя прыкладанні адчувалі гэта на сабе.
Неэфектыўнасць у захоўванні і запытах. Часта дадзеныя захоўваліся ў некаторай структуры, якая была зручная для адных сцэнараў, але не падыходзіла для іншых. Індэксы паскаралі адны аперацыі, але маглі запавольваць іншыя.
Частка праблем знялі зробленыя спехам кэшы і read-рэплікі на базы (пра гэта будзе асобны артыкул), але яны толькі дазволілі выйграць час і прынцыпова праблему не вырашалі.
Праблемай была наяўнасць самога маналіта. Следствы былі такія:
Адзіныя і рэдкія рэлізы.
Складанасць у сумеснай распрацоўцы вялікай колькасці людзей.
Немагчымасць прыўносіць новыя тэхналогіі, новыя фрэймворкі і бібліятэкі.
Перад вамі асноўныя блокі маналіта Dodo IS 2016 года, а крыху ніжэй расшыфроўка іх асноўных задач.
Каса Дастаўкі. Улік кур'ераў, выдача заказаў кур'ерам. Кантакт Цэнтр. Прыём заказаў праз аператара. сайт. Нашыя сайты (dodopizza.ru, dodopizza.co.uk, dodopizza.by і г.д.). Аўт. Сэрвіс аўтарызацыі і аўтэнтыфікацыі для бэкофіса. трэкер. Трэкер заказаў на кухні. Сэрвіс адзнакі статутаў гатовасці пры падрыхтоўцы замовы. Каса Рэстарана. Прыём заказаў у рэстаране, інтэрфейсы касіра. Экспарт. Выгрузка справаздач у 1C для бухгалтэрыі. Абвесткі і накладныя. Галасавыя каманды на кухні (напрыклад, "Паступіла новая піца") + друк накладных для кур'ераў. Мэнэджэр Змены. Інтэрфейсы для працы мэнэджара змены: спіс заказаў, графікі прадукцыйнасці, выснова на змену супрацоўнікаў. Мэнэджэр Офіса. Інтэрфейсы для працы франчайзі і кіраўніка: прыём супрацоўнікаў, справаздачы па працы піцэрыі. Табло Рэстарана. Адлюстраванне меню на тэлевізарах у піцэрыях. Адмінка. Настройкі ў канкрэтнай піцэрыі: меню, цэны, улік, прамакоды, акцыі, банеры для сайта і г.д. Асабісты Кабінет Супрацоўніка. Графікі працы супрацоўнікаў, інфармацыя аб супрацоўніках. Табло Матывацыі Кухні. Асобны экран, які вісіць на кухні і адлюстроўвае хуткасць працы піцамэйкераў. камунікацыя. Адпраўка sms і email. FileStorage. Уласны сервіс для прыёму і выдачы статычных файлаў.
Першыя спробы вырашыць праблемы дапамаглі нам, але сталі толькі часовай перадышкай. Яны не сталі сістэмнымі рашэннямі, таму было зразумела, што з базамі трэба нешта зрабіць. Напрыклад, падзяліць агульную базу на некалькі больш спецыялізаваных.
Пачынаем разгружаць маналіт: аддзяленне Auth і Трэкера
Асноўныя сэрвісы, якія тады больш за іншых запісвалі і счытвалі з базы:
Auth. Сэрвіс аўтарызацыі і аўтэнтыфікацыі для бэкофіса.
Трэкер. Трэкер заказаў на кухні. Сэрвіс адзнакі статутаў гатовасці пры падрыхтоўцы замовы.
Чым займаецца Auth
Auth – гэта сэрвіс, праз які карыстачы лагіняцца ў бэкофіс (на кліенцкай частцы асобны незалежны ўваход). Таксама да яго звяртаюцца ў запыце, каб упэўніцца, што ёсць патрэбныя правы на доступ, і што гэтыя правы не змяніліся з апошняга ўваходу. Праз яго ж адбываецца ўваход прылад у піцэрыі.
Напрыклад, нам хочацца адкрыць на тэлевізары, якая вісіць у зале, табло са статусамі гатовых заказаў. Тады мы адчыняем auth.dodopizza.ru, выбіраемы «Уваход як прылада», з'яўляецца код, які можна занесці ў адмысловай старонцы на кампутары мэнэджара змены, паказаўшы тып прылады (дэвайса). Тэлевізар сам пяройдзе на патрэбны інтэрфейс сваёй піцэрыі і пачне адлюстроўваць там імёны кліентаў, замовы якіх гатовыя.
Адкуль нагрузкі?
Кожны залагінены карыстач бэкофіса на кожны запыт ходзіць у базу, у табліцу карыстачоў, праз sql-запыт выцягвае адтуль карыстача і правярае, ці ёсць у яго патрэбныя доступы і правы на гэтую старонку.
Кожная з прылад робіць тое ж самае толькі з табліцай прылад, правяраючы сваю ролю і свае доступы. Вялікая колькасць запытаў у майстар-базу прыводзіць да яе загрузкі і марнаванні рэсурсаў агульнай базы на гэтыя аперацыі.
Разгружаем Auth
У Auth ізаляваны дамен, гэта значыць дадзеныя аб карыстачах, лагінах ці прыладах паступаюць у сэрвіс (пакуль будучы) і тамака застаюцца. Калі яны камусьці спатрэбяцца, то ён пайдзе ў гэты сэрвіс за дадзенымі.
БЫЛО. Схема працы першапачаткова была такой:
Хочацца крыху патлумачыць, як гэта працавала:
Запыт звонку прыходзіць на бэкэнд (там Asp.Net MVC), прыносіць з сабой куку сесіі, якая выкарыстоўваецца для атрымання сесійных дадзеных з Redis(1). У ёй альбо ёсць інфармацыя аб доступах, і тады доступ у кантролер адчынены (3,4), альбо не.
Калі доступу няма, трэба прайсці працэдуру аўтарызацыі. Тут для спрашчэння яна паказана як частка шляху ў тым жа атрыбуце, хаця гэта пераход на старонку лагіна. У выпадку пазітыўнага сцэнара мы атрымаем правільна запоўненую сесію і пяройдзем у Backoffice Controller.
Калі дадзеныя ёсць, тое трэба праверыць іх на актуальнасць у базе карыстача. Ці не змянілася яго роля, ці не трэба яго не пускаць зараз на старонку. У гэтым выпадку пасля атрымання сесіі (1) трэба напроста схадзіць у базу і праверыць доступы карыстача з дапамогай пласта логікі аўтэнтыфікацыі (2). Далей альбо на лагін-старонку, альбо пераход у кантролер. Такая вось простая сістэма, але пры гэтым не зусім стандартная.
Калі ўсе працэдуры пройдзены, то прапускаем далей у логіцы ў кантролерах і метадах.
Дадзеныя карыстачоў аддзеленыя ад усіх іншых дадзеных, яны захоўваюцца ў асобнай табліцы membership, функцыі з пласта логікі AuthService суцэль могуць стаць api-метадамі. Межы дамена вызначаны цалкам выразна: карыстачы, іх ролі, дадзеныя аб доступах, выдача і водгук доступаў. Усё выглядае так, што можна вынесці ў асобны сервіс.
СТАЛА. Так і зрабілі:
Такі падыход мае шэраг праблем. Напрыклад, выклік метаду ўнутры працэсу – не тое ж самае, што выклік па http знешняга сэрвісу. Латэнсі, надзейнасць, падтрымлівальнасць, празрыстасць аперацыі зусім іншыя. Падрабязней менавіта пра такія праблемы расказваў Андрэй Марэўскі ў сваім дакладзе "50 адценняў мікрасэрвісаў".
Сэрвіс аўтэнтыфікацыі і з ім сэрвіс прылад выкарыстоўваюцца для бэкофіса, гэта значыць для сэрвісаў і інтэрфейсаў, выкарыстоўваных на вытворчасці. Аўтэнтыфікацыя для кліенцкіх сэрвісаў (накшталт сайта або мабільнага дадатку) адбываецца асобна без выкарыстання Auth. Аддзяленне заняло каля года, а зараз мы зноў займаемся гэтай тэмай, пераводзячы сістэму ўжо на новыя сэрвісы аўтэнтыфікацыі (са стандартнымі пратаколамі).
Чаму аддзяленне працягвалася так доўга?
Па дарозе было мноства праблем, якія запавольвалі:
Нам жадалася перавесці дадзеныя аб карыстачах, прыладах і аўтэнтыфікацыі з баз па краіне ў адну. Для гэтага прыйшлося пераводзіць усе табліцы і выкарыстанне з ідэнтыфікатара int на глабальны ідэнтыфікатар UUId (нядаўна перапрацоўвалі гэты код Раман Букін «Uuid – вялікая гісторыя маленькай структуры» і open-source праект Першабытныя). Захоўванне дадзеных па карыстальніках (бо гэта персанальная інфармацыя) мае свае абмежаванні і для некаторых краін трэба захоўваць іх асобна. Але глабальны ідэнтыфікатар карыстальніка мусіць быць.
Шмат табліц у базе мае аўдыт інфармацыю аб тым карыстальніку, які здзейсніў аперацыю. Гэта запатрабавала дадатковага механізму, каб была кансістэнтнасць.
Пасля стварэння api-сэрвісаў быў доўгі і паступовы перыяд пераводу на іншую сістэму. Пераключэнні павінны былі адбывацца бясшвоўна для карыстачоў і патрабавалі ручной працы.
Схема рэгістрацыі прылады ў піцэрыі:
Агульная архітэктура пасля вылучэння Auth і Devices-сэрвісу:
Заўвага. На 2020 год мы працуем над новай версіяй Auth, які заснаваны на стандарце аўтарызацыі OAuth 2.0. Гэты стандарт даволі складаны, але спатрэбіцца для распрацоўкі сэрвісу скразной аўтэнтыфікацыі. У артыкуле «Тонкасці аўтарызацыі: агляд тэхналогіі OAuth 2.0» мы Аляксей Чарняеў пастараўся распавесці аб стандарце максімальна проста і зразумела, каб вы зэканомілі час на яго вывучэнне.
Чым займаецца Трэкер
Цяпер пра другі з нагружаных сэрвісаў. Трэкер выконвае дваістую ролю:
З аднаго боку, яго задача - паказваць супрацоўнікам на кухні, якія заказы цяпер у працы, якія прадукты зараз трэба рыхтаваць.
З іншага боку - аблічбоўваць усе працэсы на кухні.
Калі ў замове з'яўляецца новы прадукт (напрыклад, піца), ён трапляе на станцыю трэкера "Раскатка". На гэтай станцыі стаіць піцамэйкер, які бярэ плюшку патрэбнага памеру і раскочвае яе, пасля чаго адзначае на планшэце трэкера, што выканаў сваю задачу і перадае раскочаную аснову цеста на наступную станцыю - "Начыненне".
Там наступны піцамэйкер начыняе піцу, затым адзначае на планшэце, што выканаў сваю задачу і ставіць піцу ў печ (гэта таксама асобная станцыя, якую трэба адзначыць на планшэце). Такая сістэма была з самага пачатку ў Дадо і самага пачатку існавання Dodo IS. Яна дазваляе цалкам адсочваць і аблічбоўваць усе аперацыі. Акрамя таго трэкер падказвае, як рыхтаваць той ці іншы прадукт, праводзіць кожны выгляд прадукта па сваіх схемах выраба, захоўвае аптымальны час падрыхтоўкі прадукта і трэкае ўсе аперацыі над прадуктам.
Так выглядае экран планшэта на станцыі трэкера «Раскатка»
Адкуль нагрузкі?
У кожнай з піцэрый прыкладна па пяць планшэтаў з трэкерам. У 2016 годзе ў нас было больш за 100 піцэрый (а зараз больш за 600). Кожны з планшэтаў робіць раз у 10 секунд запыт на бэкэнд і выграбае дадзеныя з табліцы замовы (звязак з кліентам і адрасам), складу замовы (звязак з прадуктам і ўказанне колькасці), табліцы ўліку матывацыі (у ёй трэкаецца час націску). Калі піцамэйкер націскае на прадукт на трэкеры, адбываецца абнаўленне запісаў ва ўсіх гэтых табліцах. Табліца замовы агульная, у яе ж адначасова ідуць устаўкі пры прыняцці замовы, абнаўленні ад іншых частак сістэмы і шматлікія счытванні, напрыклад, на тэлевізары, які вісіць у піцэрыі і паказвае гатовыя замовы кліентам.
У перыяд барацьбы з нагрузкамі, калі ўсё і ўся кэшавалася і пераводзілася на асінхронную рэпліку базы, гэтыя аперацыі з трэкерам працягнулі хадзіць у майстар-базу. Тут не павінна быць ніякага адставання, дадзеныя павінны быць актуальнымі, рассінхран недапушчальны.
Таксама адсутнасць уласных табліц і індэксаў на іх не дазваляла напісаць больш спецыфічныя запыты, заменчаныя пад сваё выкарыстанне. Для прыкладу, трэкеру можа быць эфектыўна мець індэкс на піцэрыю на табліцы заказаў. Мы заўсёды выграбаем з базы трэкера заказы па піцэрыі. Пры гэтым для прыёму замовы не так важна, у якую піцэрыю ён падае, важней, які кліент зрабіў гэтую замову. А значыць тамака патрэбен азначнік па кліенту. Яшчэ для трэкера ў табліцы замовы не абавязкова захоўваць id надрукаванага чэка або звязаныя з замовай бонусныя акцыі. Гэтая інфармацыя наш сэрвіс трэкера не цікавіць. У агульнай маналітнай базе табліцы маглі быць толькі кампрамісным варыянтам паміж усімі карыстальнікамі. Гэта было адной з першапачатковых праблем.
БЫЛО. Першапачаткова архітэктура была такая:
Нават пасля вылучэння ў асобныя працэсы большая частка кодавай базы заставалася агульнай для розных сэрвісаў. Усё, што ніжэй за кантролераў, было адзіным і жыло ў адным рэпазітары. Выкарыстоўваліся агульныя метады сэрвісаў, рэпазітароў, агульная база, у якой ляжалі агульныя табліцы.
Разгружаем Трэкер
Галоўная праблема з трэкерам у тым, што дадзеныя павінны сінхранізавацца паміж рознымі базамі. Гэта ж і галоўнае яго адрозненне ад падзелу Auth-сэрвісу, замова і яго статут могуць змяняцца і павінны адлюстроўвацца ў розных сэрвісах.
Мы прымаем заказ на Касе Рэстарана (гэта сэрвіс), ён захоўваецца ў базе ў статусе "Прыняты". Пасля гэтага ён павінен патрапіць на трэкер, дзе яшчэ некалькі разоў зменіць свой статус: ад "Кухня" да "Упакаваны". Пры гэтым з замовай могуць адбывацца нейкія вонкавыя ўздзеянні ад Касы ці інтэрфейсу Менеджара змены. Прывяду ў табліцы статусы замовы з іх апісаннем:
Схема змены статутаў замовы выглядае так:
Статусы мяняюцца паміж рознымі сістэмамі. І тут трэкер не з'яўляецца канчатковай сістэмай, у якой замыкаюцца дадзеныя. Мы бачылі некалькі магчымых падыходаў для падзелу ў такім выпадку:
Канцэнтруем усе дзеянні замовы ў адным сэрвісе. У нашым выпадку гэты варыянт патрабуе занадта вялікага сэрвісу па працы з замовай. Калі б мы спыніліся на ім, то атрымаўся б другі маналіт. Праблемы б мы не вырашылі.
Адна сістэма робіць выклік у іншую. Другі варыянт ужо цікавейшы. Але пры ім магчымыя ланцужкі выклікаў (каскадныя збоі), складнасць кампанентаў вышэй, кіраваць гэтым складаней.
Які арганізуецца падзеі, і кожны сэрвіс абменьваецца з іншым праз гэтыя падзеі. У выніку быў абраны менавіта трэці варыянт, па якім усе сэрвісы пачынаюць абменьвацца падзеямі сябар з сябрам.
Тое, што мы абралі трэці варыянт азначала, што для трэкера будзе свая база, а на кожную змену замовы ён будзе дасылаць падзею пра гэта, на якую падпісваюцца іншыя сэрвісы і якая ў тым ліку трапляе ў майстар-базу. Для гэтага нам патрэбен быў некаторы сэрвіс, які забяспечыць дастаўку паведамленняў паміж сэрвісамі.
Да таго часу ў нас у стэку ўжо быў RabbitMQ, адсюль і выніковае рашэнне выкарыстоўваць яго як брокер паведамленняў. На схеме паказаны пераход замовы ад Касы Рэстарана праз Трэкер, дзе ён мяняе свае статусы і адлюстраванне яго на інтэрфейсе Заказы мэнэджара. СТАЛА:
Шлях замовы па кроках
Шлях замовы пачынаецца на адным з сэрвісаў крыніц замовы. Тут гэта Каса Рэстарана:
На Касе цалкам гатовы заказ, і яго час адправіць на трэкер. Кідаецца падзея, на якую падпісаны трэкер.
Трэкер, прымаючы сабе замову, захоўвае яго ў сваю ўласную базу, робячы пры гэтым падзею «ЗаказПрынятыТрэкерам» і пасылаючы яго ў RMQ.
У шыне падзей на заказ ужо падпісаны некалькі апрацоўшчыкаў. Для нас важны той, які робіць сінхранізацыю з маналітнай базай.
Апрацоўшчык прымае падзею, выбірае з яго значныя для яго дадзеныя: у нашым выпадку гэта статут замовы «ПрынятыТрэкерам» і абнаўляе сваю сутнасць замовы ў асноўнай базе.
Калі камусьці патрэбна замова менавіта з маналітнай табліцы orders, то можна лічыць яго і адтуль. Напрыклад, такое трэба інтэрфейсу Заказы ў Мэнэджары Змены:
Усе іншыя сэрвісы таксама могуць падпісацца на падзеі замовы з трэкера, каб выкарыстоўваць іх для сябе.
Калі праз некаторы час замова бярэцца ў працу, то яго статут спачатку змяняецца ў сваёй базе (базе Трэкера), а потым адразу генеруецца падзея «ЗаказВРабоце». Яно таксама пападае ў RMQ, адкуль сінхранізуецца ў маналітнай базе і дастаўляецца іншым сэрвісам. На гэтым шляху могуць быць розныя праблемы, падрабязней пра іх можна паглядзець у дакладзе Жэні Пяшкова пра дэталі рэалізацыі Eventual Consistency у Трэкеры.
Выніковая архітэктура пасля змен у Auth і Трэкеры
Падводзячы прамежкавы вынік: першапачаткова ў мяне была думка спакаваць дзевяцігадовую гісторыю сістэмы Dodo IS у адзін артыкул. Жадалася хутка і проста распавесці аб этапах эвалюцыі. Аднак сеўшы за матэрыял, я зразумеў, што ўсё значна складаней і цікавей, чым здаецца.
Разважаючы над карысцю (ці яе адсутнасцю) ад такога матэрыялу, я прыйшоў да высновы, што бесперапыннае развіццё немагчыма без паўнавартасных летапісаў падзей, разгорнутых рэтраспектыў і аналізу сваіх мінулых рашэнняў.
Спадзяюся, што вам было карысна і цікава даведацца пра наш шлях. Цяпер я стаю перад выбарам, якую частку сістэмы Dodo IS апісаць у наступным артыкуле: пішыце ў каментарах ці галасуйце.
Толькі зарэгістраваныя карыстачы могуць удзельнічаць у апытанні. Увайдзіце, Калі ласка.
Аб якой частцы Dodo IS вам бы жадалася пазнаць у наступным артыкуле?
24,1%Ранні маналіт у Dodo IS (2011-2015 гады)14
24,1%Першыя праблемы і іх рашэнні (2015-2016 гады)14
20,7%Шлях кліенцкай часткі: фасад над базай (2016-2017 гады)12