Проектування масштабованої інфраструктури задніх місць з нуля

Створення дизайну готової майбутньої платформи з нуля дуже затребувано в наші дні, але обернути голову навколо величезної інформації, наявної в Інтернеті, непросто. Таким чином, ми будемо будувати повнофункціональний масштабований допоміжний крок за кроком у цій багатоскладовій серії.

Я створив серію youtube з цього блогу, оскільки отримав так багато запитів. Будь ласка, дотримуйтесь альфакоду мого каналу youtube для серії лекцій з архітектури мікросервісів.

Посилання на першу серію курсових збоїв: https://www.youtube.com/playlist?list=PLZBNtT95PIW3BPNYF5pYOi4MJjg_boXCG

Розробляючи першу версію програми, у вас часто не виникає проблем зі масштабуванням. Більше того, використання розподіленої архітектури уповільнює розвиток. Це може стати головною проблемою для стартапів, найбільшою проблемою яких є швидке розвиток бізнес-моделі та скорочення часу на ринку. Але оскільки ви тут, я припускаю, що ви це вже знаєте. Давайте стрибаємо прямо в нього, пам'ятаючи про наступні цілі:

  1. Розподіліть розробку API: Система повинна бути спроектована таким чином, що над нею одночасно можуть працювати декілька команд, і одна команда не повинна стати вузьким місцем, а також не потрібно мати досвід у всьому застосуванні для створення оптимізованих кінцевих точок.
  2. Підтримка декількох мов: для того, щоб скористатися новими технологіями, кожна функціональна частина системи повинна мати можливість підтримувати бажану мову, обрану для цієї функції.
  3. Мінімізуйте затримку: будь-яка архітектура, яку ми пропонуємо, завжди повинна намагатися мінімізувати час відгуку клієнта.
  4. Мінімізуйте ризики розгортання: різні функціональні компоненти системи повинні мати можливість розгортатися окремо з мінімальною координацією.
  5. Звести до мінімуму апаратний слід: Система повинна намагатися оптимізувати кількість використовуваного обладнання та має бути горизонтально масштабованою.

Побудова монолітних застосувань

Давайте уявимо, що ви починали створювати абсолютно новий додаток для електронної комерції, призначений конкурувати з Amazon. Ви б почали, створивши новий проект у своєму бажаному виборі платформи, наприклад Rails, Spring Boot, Play тощо. Зазвичай він має модульну архітектуру приблизно так:

Фіг.1

Верхній шар, як правило, обробляє запити клієнта, і після деяких перевірок він пересилатиме запит на рівень обслуговування, де реалізована вся бізнес-логіка. Служба використовуватиме різні адаптери, такі як компоненти доступу до бази даних у шарі DAO, компоненти обміну повідомленнями, зовнішні API та інші сервіси в тому ж шарі, щоб підготувати результат і повернути його назад до контролера, який стажер повертає його клієнту.

Цей вид додатків, як правило, упаковується та розгортається як моноліт, що означає один великий файл. Наприклад, це буде баночка у випадку весняного завантаження та zip-файл у випадку програми Rails або Node.js. Такі програми досить поширені і мають багато переваг, їх легко зрозуміти, керувати, розробляти, тестувати та розгортати. Ви також можете їх масштабувати, виконавши декілька копій його за балансиром навантаження, і він працює досить добре до певного рівня.

На жаль, цей простий підхід має величезні обмеження, такі як:

  • Блокування мови / фреймворку: оскільки вся програма записується в єдиний технологічний стек. Не можна експериментувати з новими технологіями.
  • Складно перетворити: Коли додаток стає великим, розробнику стає важко зрозуміти таку велику базу коду.
  • Складно розповсюдити розробку API: зробити надзвичайно складно зробити спритну розробку, і значна частина часу розробника витрачається на вирішення конфліктів.
  • Розгортання як єдиний блок: Неможливо самостійно розгорнути одну зміну до одного компонента. Зміни "заручаються" іншими змінами.
  • Розвиток сповільнюється: я працював над кодовою базою, в якій було понад 50 000 класів. Самий розмір кодової бази був достатній, щоб уповільнити час IDE та час запуску, через що продуктивність погіршувалася.
  • Ресурси не оптимізовані: Деякі модулі можуть реалізувати логіку обробки зображень, що вимагає процесора, вимагаючи оптимізованих обчислювальних примірників, а інший модуль може бути вбудованою пам'яттю і найкраще підходить для оптимізованих пам'яті екземплярів. Але нам доведеться йти на компроміс у виборі обладнання. Також може статися, що для одного модуля програми потрібно масштабування, але нам доведеться запустити цілий екземпляр програми знову, оскільки ми не можемо масштабувати модуль окремо.

Чи не було б дивним, якби ми могли розбити програму на більш дрібні частини та керувати ними таким чином, щоб вона працювала як окрема програма, коли ми запускаємо її? Так, саме так ми і зробимо далі!

Архітектура мікросервісів

Багато організацій, таких як Amazon, Facebook, Twitter, eBay та Netflix, вирішили цю проблему, прийнявши те, що на сьогоднішній день відоме як модель архітектури мікросервісів. Він вирішує цю проблему, поділяючи її на менші підпроблеми, які поділяються поділом і перемагають у світі розробників. Подивіться на малюнок 1 уважно, ми виріжемо з нього вертикальні фрагменти і створимо менші взаємопов’язані сервіси. Кожен фрагмент буде реалізовувати різні функції, такі як управління кошиком, управління користувачами та управління замовленнями тощо. Кожна служба може бути написана будь-якою мовою / рамкою і може мати стійкість поліглота, що відповідає випадку використання. Легко-гороховий так?

Але зачекайте! Ми також хотіли, щоб він поводився як один додаток до клієнта в іншому випадку, клієнту доведеться мати справу з усією складністю, пов’язаною з цією архітектурою, як агрегування даних різних служб, підтримка такої кількості кінцевих точок, підвищення чатовості клієнта та сервера, окрема аутентифікація для кожної послуги. Залежність клієнта від мікропослуг безпосередньо ускладнює переформатування послуг також. Інтуїтивно зрозумілий спосіб зробити це - приховати ці служби за новим рівнем обслуговування та надати API, який підходить для кожного клієнта. Цей рівень обслуговування агрегатора також відомий як шлюз API і є загальним способом вирішення цієї проблеми.

Шаблон архітектури мікросервісів на основі шлюзу API

Усі запити клієнтів спочатку проходять через шлюз API. Потім він спрямовує запити до відповідної мікросервісу. Шлюз API часто обробляє запит, викликаючи кілька мікросервісів та агрегуючи результати. У ньому можуть бути інші обов'язки, такі як аутентифікація, моніторинг, балансування навантаження, кешування та обробка статичного відгуку. Оскільки цей шлюз надає специфічні для клієнта API, він зменшує кількість зворотних поїздок між клієнтом і додатком, що зменшує затримку в мережі, а також спрощує код клієнта.

Функціональне розкладання моноліту буде змінюватися залежно від випадку використання. Amazon використовує більше 100 мікросервісів для відображення однієї сторінки продуктів, тоді як Netflix має понад 600 мікросервісів, які керують їхніми резервними копіями. Мікросервіси, перелічені на наведеній вище діаграмі, дають вам уявлення про те, як масштабується додаток для електронної комерції має бути розкладене, але може знадобитися більш ретельне спостереження перед його застосуванням для виробництва.

Немає такого поняття, як безкоштовний обід. Мікросервіси приносять із собою деякі складні проблеми, як-от:

  • Проблеми з розподіленими обчислювальними технологіями: оскільки різні мікросервіси повинні працювати в розподіленому середовищі, нам потрібно буде опікуватися цими помилками розподілених обчислень. Коротше кажучи, ми повинні припустити, що поведінка та місця розташування компонентів нашої системи постійно змінюватимуться.
  • Віддалені дзвінки коштують дорого: розробникам потрібно вибрати та впровадити ефективний механізм міжпроцесорного зв’язку.
  • Розподілені транзакції: комерційні операції, що оновлюють кілька суб'єктів господарювання, повинні покладатися на можливу послідовність використання ACID.
  • Поводження з послугою Недоступність: нам потрібно буде розробити нашу систему для обліку недоступності або повільності послуг. Все постійно провалюється.
  • Реалізація функцій, що охоплюють кілька служб.
  • Інтеграційне тестування та управління змінами стають важкими.

Звичайно, керування складністю мікросервісів вручну незабаром почне виходити з рук. Для створення автоматизованої та самолікувальної розподіленої системи нам потрібно мати такі функції в нашій архітектурі.

  • Центральна конфігурація: централізована система конфігурації, яка є на зразок Zookeeper, зміни до якої динамічно застосовуються до запущених служб.
  • Відкриття служби: Кожна запущена служба повинна зареєструватися на сервері виявлення сервісу, і сервер повідомляє всіх, хто перебуває в мережі. Так само, як і звичайний додаток для чату. Ми не хочемо жорстко кодувати адресу кінцевої точки обслуговування одна до одної.
  • Балансування завантаження: балансування навантаження клієнта на стороні, щоб ви могли застосовувати складні стратегії балансування та робити кешування, дозування, толерантність, виявлення сервісу та обробку декількох протоколів.
  • Міжпроцесовий зв'язок: нам потрібно буде впровадити ефективну стратегію комунікації між процесами. Це може бути що-небудь на зразок REST або Thrift або асинхронних механізмів зв'язку на основі повідомлень, таких як AMQP або STOMP. Ми також можемо використовувати ефективні формати повідомлень, такі як буфери Avro або Protocol, оскільки це не буде використовуватися для спілкування із зовнішнім світом.
  • Аутентифікація та безпека. Нам потрібно створити систему визначення вимог аутентифікації для кожного ресурсу та відхилення запитів, які їх не задовольняють.
  • Неблокуючи IO: шлюз API обробляє запити шляхом виклику декількох серверних сервісів та агрегування результатів. З деякими запитами, такими як запит про деталі продукту, запити до сервісів, що надаються, незалежні один від одного. Щоб мінімізувати час відповіді, шлюз API повинен виконувати незалежні запити одночасно.
  • Послідова послідовність. Нам потрібно створити систему для управління бізнес-транзакціями, що охоплюють декілька послуг. Коли сервіс оновлює свою базу даних, він повинен опублікувати подію, а також повинен бути посередник повідомлень, який гарантує, що події будуть доставлені хоча б один раз абонентським службам.
  • Толерантність помилок: Ми повинні уникати ситуації, коли одна помилка каскадує в системі. Шлюз API ніколи не повинен блокувати нескінченно очікування служби нижче. Він повинен виправляти збої витончено і повертати часткові відповіді, коли це можливо.
  • Розподілені сесії: В ідеалі у нас не повинно бути жодного стану на сервері. Стан програми слід зберігати на стороні клієнта. Це один із важливих принципів служби RESTful. Але якщо ви виняток і не можете цього уникнути, завжди майте розподілені сесії. Оскільки клієнт спілкується лише з шлюзом API, нам потрібно буде запустити кілька копій його за балансиром навантаження, оскільки ми не хочемо, щоб шлюз API став вузьким місцем. Це означає, що наступні запити клієнта можуть перейти на будь-який із запущених примірників шлюзу API. Нам потрібно мати спосіб ділитися інформацією про аутентифікацію між різними екземплярами шлюзу API. Ми не хочемо, щоб клієнт повторно підтверджував автентифікацію кожного разу, коли його запит потрапляє на інший примірник шлюзу API.
  • Розподілене кешування: Ми повинні мати механізми кешування на декількох рівнях, щоб зменшити затримку клієнта. Кілька рівнів просто означає, що клієнт, шлюз API і мікросервіси повинні мати надійний механізм кешування.
  • Детальний моніторинг: Ми повинні мати можливість відслідковувати змістовні дані та статистику кожного функціонального компонента, щоб дати нам точний огляд виробництва. Необхідно спрацьовувати належних тривожних сигналів у випадку винятків або високого часу реагування.
  • Динамічна маршрутизація: шлюз API повинен мати можливість інтелектуально маршрутизувати запити до мікросервісів, якщо він не має конкретного відображення до запитуваного ресурсу. Іншими словами, зміни в шлюзі API не потрібно вимагати кожного разу, коли мікросервіс додає нову кінцеву точку на своїй стороні.
  • Автоматичне масштабування: кожен компонент нашої архітектури, включаючи шлюз API, повинен бути горизонтально масштабованим і повинен автоматично масштабуватися, коли це потрібно, навіть якщо він розміщений всередині контейнера.
  • Підтримка поліглотів: Оскільки різні мікросервіси можуть бути написані різними мовами чи рамками, система повинна забезпечувати плавні виклики послуг та вищезгадані функції незалежно від мови, на якій вона написана.
  • Плавне розгортання: Розгортання наших мікросервісів має бути швидким, незалежним та автоматизованим, якщо це можливо.
  • Незалежна платформа: Для ефективного використання апаратного забезпечення та для того, щоб наші сервіси не залежали від платформи, на якій він розміщений, нам слід розгорнути наші веб-служби всередині якогось контейнера, наприклад докера.
  • Агрегація журналу: у нас повинна бути створена система, яка автоматично зберігає агрегацію журналів із усіх мікросервісів у файловій системі. Ці журнали згодом можуть бути використані для різноманітної аналітики.

Ого! Це безліч функцій, які можна реалізувати лише для того, щоб подбати про архітектуру. Це справді того варте? І відповідь - так. Архітектура мікропослуг проходить випробування в таких змаганнях, як Netflix, яка лише споживає близько 40% світової пропускної здатності в Інтернеті.

У своєму наступному дописі я описую, як ми можемо почати розробляти наші мікросервіси. Слідкуйте за новинами та слідкуйте за мною будь-де на Youtube, щоб отримати оновлення. Бувай!