Мікросервіси
Мікросервіси — це архітектура, у якій систему будують як набір сервісів, кожен з яких можна деплоїти окремо. Визначальна властивість — незалежна деплойність, а не розмір: сервіс правильного розміру тоді, коли одна команда володіє ним від початку до кінця. Усе добре в цьому стилі випливає з тієї незалежності, а все складне — з того, що виклики між сервісами тепер ідуть через мережу.
- Визначальна риса
- Незалежний деплой
- Правило даних
- Своє сховище на сервіс
- Режим відмови
- Розподілений моноліт
Що робить сервіс мікросервісом
Забудьте на мить про «мікро» — цей склад завдав більше шкоди, ніж будь-який інший у цій темі, бо він запрошує міряти сервіси в рядках коду. Важлива ось яка властивість: чи можна змінити, протестувати й задеплоїти цей сервіс у продакшен, не деплоячи нічого іншого? Якщо так — це мікросервіс. Якщо реліз вимагає, щоб два сервіси вийшли разом, у вас один сервіс у двох репозиторіях.
З цієї перевірки випливають три речі, і саме вони є справжніми правилами стилю. Сервіс володіє своїми даними — жоден інший сервіс ніколи не читає його таблиць. Сервісом володіє одна команда, і саме це робить незалежний деплой організаційним фактом, а не технічною можливістю. І сервіс вирівняний за бізнес-спроможністю — замовлення, ціноутворення, сповіщення, — а не за технічним шаром.
На правилі вирівнювання варто затриматися, бо помилка тут тиха. Сервіси з назвами «сервіс бази даних», «сервіс валідації» та «сервіс API» — це шари з мережевими викликами між ними: кожна фіча зачіпає всі три, тож ніщо не релізиться незалежно, а повну ціну розподіленої системи ви вже сплатили за нульову незалежність. Сервіси, названі за тим, що робить бізнес, зачіпає одна фіча за раз — і саме звідти береться вся користь.
Що потрібно мати до початку
Фаулер сформулював це як обмеження за зростом: щоб користуватися мікросервісами, треба бути ось такого зросту. Список короткий, він не є предметом торгу, і кожен пункт у ньому — це те, без чого моноліт дозволяє обходитися.
Автоматичний деплой
Деплой — це пайплайн, а не людина. З двадцятьма сервісами деплой із ручним кроком трапляється у двадцять разів частіше, — і саме ручний крок зупинить незалежні релізи команд.
Спостережуваність
Розподілене трасування, зібрані логи з ID кореляції, метрики на сервіс. Без цього запит, що впав десь на чотирьох сервісах, не дебажиться — його можна лише вгадувати.
Швидке створення середовищ
Новий сервіс має опинитися в продакшені за години, а не після черги тікетів. Якщо підняти сервіс дорого, команди чіплятимуть фічі до наявних, і межі попливуть.
Команди, що володіють сервісом
Зокрема в продакшені й зокрема о третій ночі. Сервіс, який експлуатує окрема команда ops, на практиці не деплоїться незалежно, хоч би що казала схема.
Дані — найскладніше
Правило просте: кожен сервіс володіє власним сховищем, і жоден інший його не торкається. Наслідки цього правила — це і є основна робота, і вони дивують тих, хто прийшов, очікуючи складнощів із деплоєм.
Ви втрачаєте джойн. Звіт, якому потрібні дані замовлень і клієнтів, більше не може попросити обидва в бази; він питає два сервіси або читає з подання, зробленого для цього. Ви також втрачаєте транзакцію. Операція, що охоплює два сервіси, не може бути атомарною, тож «списати оплату й зарезервувати товар» стає послідовністю кроків із визначеною поведінкою на випадок, коли другий крок падає після успішного першого, — це сага з компенсаційними діями, які пишете ви.
І ви успадковуєте консистентність із часом як продуктове питання, а не технічне. Якщо товар зарезервовано за секунду до підтвердження оплати, хтось має вирішити, що бачить клієнт у цю секунду і що буде, якщо оплата потім не пройде. Це рішення належить бізнесу, його треба ухвалювати явно для кожного міжсервісного потоку, а вдавання, що його немає, і призводить до подвійних списань.
Синхронно, асинхронно і рахунок за доступність
Кожен виклик між сервісами може бути повільним, впасти або — найгірше з трьох — успішно виконатися на тому боці, повернувши вам помилку. Вибір того, як робиться кожен виклик, є основною проєктною діяльністю в цьому стилі.
Синхронний виклик зв’язує доступність. Якщо оформлення замовлення викликає білінг, а той — реєстр, і кожен доступний 99,9% часу, то оформлення тепер доступне 99,7% — гірше за будь-який компонент у ланцюзі й гірше за моноліт, який робив ту саму роботу в одному процесі. Додайте ретраї — і ви додасте навантаження саме тоді, коли сервіс нижче за потоком уже задихається; так повільна залежність стає аварією.
Асинхронне ребро розриває цей зв’язок. Якщо оформлення публікує подію, а пошук її споживає, пошук може лежати годину, і жоден клієнт не помітить цього при оформленні; він наздожене, коли повернеться. Ціна — пошук цю годину показує застарілі дані, і хтось має бути з цим згоден. Ось обмін одним реченням: синхронність купує свіжість і платить доступністю, асинхронність купує доступність і платить застарілістю.
| Механізм | Від чого захищає | Чим коштує |
|---|---|---|
| Таймаут | Того, що викликач тримає потік, поки мертва залежність відповість, і вичерпує пул. | Повільний, але справний виклик може обірватися |
| Ретрай із відступом | Тимчасових збоїв — перезапущеного пода, одного втраченого пакета. | Додане навантаження на сервіс, якому вже зле |
| Запобіжник | Ретраїв у справді мертву залежність і каскаду, що з цього піде. | Погіршена відповідь, поки він розімкнений |
| Ключ ідемпотентності | Ретраю, що списує з клієнта двічі, бо перший виклик усе-таки пройшов. | Стан, який має тримати отримувач |
Останній рядок — те, про що ніколи не попереджають джуніорів і на чому обпікалися всі сеньйори. Мережевий збій не каже вам, чи операція сталася: виклик міг пройти, а відповідь загубитися. Тому будь-який ретрай будь-чого, що змінює стан, має бути безпечним для повторення — а отже, отримувач мусить упізнавати запит, який уже обробив. Ретраї без ідемпотентності — це не стійкість, а машина для дублювання побічних ефектів.
Як це виявляється в реальній поставці
Чесний підсумок двох десятиліть практики: мікросервіси розв’язують організаційну проблему технічним механізмом. Якщо ваше обмеження в тому, що дванадцять команд не можуть релізити без узгодження, цей стиль знімає обмеження й вартий свого рахунку. Якщо ваше обмеження в тому, що код заплутаний, деплой страшний або база повільна, цей стиль додає до проблеми мережу й виставляє за це щомісячний рахунок.
Де вони вироджуються
- Спільна база даних — та єдина зміна, що перетворює набір сервісів на розподілений моноліт.
- Сервіси, названі за технічними шарами: кожна фіча зачіпає всі, і ніщо не релізиться незалежно.
- Релізи, що вимагають випуску кількох сервісів у фіксованому порядку, — це і є визначення відсутньої незалежності.
- Довгі ланцюги синхронних викликів, де доступність множиться вниз, нижче за монолітну.
- Ретраї без ключів ідемпотентності дублюють побічні ефекти саме за тих умов, заради яких ретраї й існують.
- Один сервіс на розробника дає топологію, що відображає найм, а не предметну область.
- Відсутність розподіленого трасування перетворює кожен міжсервісний інцидент на археологію.
Коли це застосовувати
Застосовуйте, коли
- Кілька команд справді блокують одна одну на спільному деплої, і ви виміряли ціну цього.
- Компоненти мають справді різні профілі масштабування, цілі доступності чи межі відповідності.
- Безперервний деплой, розподілене трасування й швидке створення середовищ уже працюють.
- Команди можуть самі експлуатувати свій сервіс у продакшені, зокрема поза робочими годинами.
- Бізнес може відповісти, що має бачити користувач, поки два сервіси тимчасово неузгоджені.
Уникайте, коли
- Невелика команда — проблеми координації, яку це розв’язує, ще немає, а рахунок усе одно прийде.
- Межі предметної області ще не зрозумілі, тож межі сервісів були б здогадами, які дорого пересувати.
- Справжня проблема — заплутаний код: поділ по наявній зв’язності дає його розподілену версію.
- Деплой ще ручний — це множиться на кількість сервісів, а не лишається сталим.
- Сувора консистентність усієї системи є бізнес-вимогою, а не звичкою.
Було корисно?
Поділіться з тим, хто працює над тією ж задачею.