Яка найбільш прийнята стратегія транзакцій для мікросервісів


80

Одне з найважливіших проблем, яке, як я бачив, трапляється в системі з мікропослугами - це те, як працюють транзакції, коли вони охоплюють різні служби. В рамках нашої власної архітектури ми використовували розподілені транзакції для вирішення цього питання, але вони мають свої проблеми. Особливо досі тупики були болем.

Іншим варіантом здається, що це якийсь на замовлення менеджер транзакцій, який знає потоки у вашій системі і піклується про відкати для вас як фоновий процес, що охоплює всю вашу систему (тому він скаже іншим службам про відкат а якщо вони знищені, повідомте про це пізніше).

Чи є інший, прийнятий варіант? І те, і інше, має свої недоліки. Перший може спричинити тупикові місця та купу інших проблем, другий може призвести до невідповідності даних. Чи є кращі варіанти?


Щоб переконатися, чи є ті мікросервіси, якими користуються багато клієнтів одночасно, або лише один за одним?
Марсель

2
Я намагався поставити це питання агностично до цього, Марсель. Але припустимо, що ми використовуємо систему, достатньо велику, щоб зробити те і інше, і ми хочемо мати архітектуру, яка підтримує обоє.
Крістоф

4
Це погано сформульоване, але дуже важливе питання. Коли більшість людей думає про "транзакції", вони думають майже виключно про послідовність, а не про аспекти атомності, ізоляції чи довговічності транзакцій. Насправді слід сказати питання: "Як створити сумісну з ACID систему з архітектурою мікросервісів. Тільки реалізація послідовності, а не решта ACID не дуже корисна. Якщо ви не любите погані дані.
Джефф Фішер,

10
Ви завжди можете спробувати змінити моє запитання, щоб зробити його менш "погано сформульованим", Джефф Фішер.
Крістоф

Це питання та обговорення тісно пов’язані з цим іншим питанням про СО .
Пауло Мерсон

Відповіді:


42

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

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

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


5
Ви припускаєте, що повернення коштів ніколи не може відбутися.
Саньєєв Кумар Дангі

9
@SanjeevKumarDangi це менш ймовірно, і навіть якщо він не вдається, його можна легко обробляти, оскільки рахунок рахунку холдингу та фактичний рахунок перебуває під контролем банку.
gldraphael

30

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

Це одна з причин, коли дуже важко розділити систему на сервіси, поки ви її повністю не спроектували. Що в сучасному світі, мабуть, означає, що це написано ...


37
Такий підхід цілком може призвести до об'єднання всіх мікросервісів до єдиного монолітного додатку.
Слава Фомін ІІ

4
Це правильний підхід. Це насправді просто: якщо вам потрібна трансакція між службами, ваші служби помиляються: переробіть їх! @SlavaFominII те, що ви говорите, справедливо лише в тому випадку, якщо ви не знаєте, як створити мікросервісну систему. Якщо ви опинитесь проти боротьби з мікропослугами, не робіть цього, ваш моноліт стане кращим, ніж поганий дизайн мікросервісу. Тільки коли ви знайдете правильні межі обслуговування, це коли ви повинні розділити моноліт на послуги. В іншому випадку використання мікросервісів не є правильним архітектурним вибором, це лише слідування за рекламою.
Francesc Castells

@FrancescCastells Що робити, якщо наші сервіси дійсно вимагають трансакцій між іншими послугами, ви маєте на увазі, що ми повинні ігнорувати обмежені контексти та моделювати наші послуги таким чином, що вони закінчуються як одна операція? Я новачок в microservices, по- , як і раніше читає так , вибачте моє запитання , якщо це звучить наївно ....: D: D
Більбо Беггінс

@BilboBaggins Я маю на увазі протилежне ігнорування обмежених контекстів. За визначенням транзакції відбуваються в обмеженому контексті, а не в декількох з них. Тому, якщо ви правильно розробили свої мікросервіси разом із обмеженими контекстами, ваші транзакції не повинні охоплювати декілька послуг. Зауважте, що певний час вам не потрібні транзакції, а краще поводження з можливою послідовністю та належними компенсаційними діями, коли справи йдуть неправильно.
Francesc Castells

Я не розумію, чи є шанс, що ми можемо обговорити це у чаті?
Більбо Баггінс

17

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

Мікросервіси запроваджують можливі проблеми узгодженості через похвальне наполягання на децентралізованому управлінні даними. За допомогою моноліту ви можете оновити купу речей разом за одну транзакцію. Мікросервіси потребують декількох ресурсів для оновлення, і розподілені транзакції нахмуріться (з поважної причини). Тож зараз розробникам потрібно знати про проблеми послідовності та придумувати, як виявити, коли речі не синхронізовані, перш ніж робити що-небудь, код пошкодує.

Але, можливо, у вашому випадку ви можете пожертвувати послідовністю в позиції доступності

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

Однак я також запитую себе, чи існує стратегія розподілених транзакцій в мікросервісах, але, можливо, вартість занадто висока. Я хотів дати вам два мої центи за завжди чудову статтю Мартіна Фаулера та теорему CAP .


1
У розподілених бізнес-операціях послідовність іноді вирішується шляхом додавання рівня оркестрації, як двигун робочого процесу або ретельно розроблена черга. Цей шар обробляє всі двофазні фіксації та відкочування назад, а також дозволяє мікросервісам зосередитися на конкретній бізнес-логіці. Але повертаючись до CAP, інвестування у доступність та послідовність робить продуктивність жертвою. Як мікросервіси порівнюють з безліччю роз'єднаних класів, що складають ваш бізнес в OOP?
Грег

Ви можете використовувати RAFT через мікросервіси для вирішення послідовності FWIW
f0ster

1
+1. Мені часто цікаво, чому в контексті мікросервісів взагалі бажані транзакції, якщо всі "витягуючі" погляди даних можуть бути реалізовані як матеріалізовані подання. Наприклад, якщо одна мікросервіс реалізує дебети з одного рахунку, а інша кредити на інший рахунок, перегляди залишків враховуватимуть лише пари кредитів та дебетів, де незрівняні кредити та дебети все ще будуть у буферах до матеріалізованого подання.
Sentinel

16

Як запропоновано принаймні в одному з відповідей тут, але також і в інших мережах в Інтернеті, можна створити одну мікросервіс, який зберігатиме об'єкти разом у межах звичайної транзакції, якщо вам потрібна узгодженість між двома сутностями.

Але в той же час у вас цілком може виникнути ситуація, коли суб’єкти дійсно не належать до однієї мікросервіси, наприклад, записи про продаж та записи замовлень (коли ви замовляєте щось для здійснення продажу). У таких випадках вам може знадобитися спосіб забезпечення узгодженості між двома мікропослугами.

Традиційно розподілені транзакції використовуються, і на мій досвід, вони працюють добре, поки не набули масштабу, коли блокування стає проблемою. Ви можете розслабити замок так, що дійсно лише відповідні ресурси (наприклад, проданий товар) "блокуються" за допомогою зміни стану, але саме тут воно починає складніше, оскільки ви в'їжджаєте на територію, де потрібно побудувати всі Логіка робити це самостійно, а не мати, скажімо, база даних обробляти це за вас.

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

Є продукти, які можна зафіксувати у вашій системі, які дбають про послідовність. Двигун бізнес-процесів є хорошим прикладом, і вони, як правило, обробляють узгодженість зрештою і за допомогою компенсації. Інші продукти працюють аналогічно. Зазвичай у вас виявляється шар програмного забезпечення біля клієнта (клієнтів), який займається послідовністю, транзакціями та закликає (мікро) послуги зробити фактичну обробку бізнесу . Одним із таких продуктів є загальний роз'єм JCA, який можна використовувати з рішеннями Java EE (для прозорості: я автор). Дивіться http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html для отримання більш детальної інформації та більш глибокого обговорення порушених тут питань.

Інший спосіб обробки транзакцій та послідовності - це перетворення дзвінка на мікросервіс у виклик до чогось транзакційного, наприклад черги повідомлень. Візьміть приклад запису продажів / записів замовлень зверху - ви можете просто дозволити мікросервісу продажів надсилати повідомлення до системи замовлень, яка здійснюється в тій же транзакції, яка записує продаж у базу даних. Результат - асинхронне рішення, яке дуже добре масштабує . Використовуючи такі технології, як веб-розетки, можна навіть подолати проблему блокування, яка часто пов'язана зі збільшенням масштабів асинхронних рішень. Докладніші ідеї щодо подібних моделей дивіться в іншій моїй статті: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .

Яке б рішення ви не вирішили вибрати, важливо визнати, що лише невелика частина вашої системи буде писати речі, які повинні бути послідовними - більшість доступу, ймовірно, доступні лише для читання. З цієї причини побудуйте управління транзакціями лише у відповідних частинах системи, щоб воно все ще могло добре масштабуватися.


Тим часом я б сказав, що слід серйозно розглянути можливість перетворення процесу на асинхронний, де кожен крок у процесі є повністю транзакційним. Ознайомтесь тут детальніше: blog.maxant.co.uk/pebble/2018/02/18/1518974314273.html
Ant

"Зробіть приклад запису продажів / запису замовлення зверху - ви можете просто дозволити мікросервісу продажів надсилати повідомлення системі замовлень, яке здійснюється в тій же транзакції, яка записує продаж у базу даних." Не впевнений, що те, що ви пропонуєте, в основному є розподіленою транзакцією, що сказало, як би ви попрацювали зі сценарієм відката в цьому випадку? Наприклад, повідомлення привласнюється до черги повідомлень, але повертається назад на стороні БД.
Сактів

@sactiw не впевнений, чи я міг би мати на увазі двофазну фіксацію, але я б уникав цього зараз, а замість цього записую свої бізнес-дані, і те, що повідомлення потрібно додати до черги, за одну транзакцію до моєї бази даних мікросервісу . Потім команда "факт" ака "команда" обробляється асинхронізацією після здійснення транзакції, використовуючи автоматизований механізм повторного спроби. Для прикладу дивіться статтю в блозі з 2018-03-10. Якщо це можливо, уникайте відкату чи компенсації на користь стратегії форварду, оскільки це легше здійснити.
Мурашка Кучера

1

У мікропослугах є три способи досягнення узгодженості між різн. послуги:

  1. Оркестрація - один процес, який керує транзакцією та відкатом між службами.

  2. Хореографія - Служба передає повідомлення один одному і нарешті дістанеться до постійного стану.

  3. Гібрид - змішування вищевказаних.

Для повного читання перейдіть за посиланням: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c


1

Є багато рішень, які компромісують більше, ніж мені подобається. Зрозуміло, якщо ваш спосіб використання складний, наприклад, переміщення грошей між різними банками, більш приємні альтернативи можуть бути неможливими. Але давайте подивимось, що ми можемо зробити за загальним сценарієм, коли використання мікросервісів заважає нашим потенційним транзакціям із базами даних.

Варіант 1: Уникайте необхідності угод, якщо це можливо

Очевидно і згадувалося раніше, але ідеально, якщо ми можемо ним керувати. Чи належали компоненти насправді в одній мікросервісі? Або ми можемо переробити такі системи, щоб транзакція стала непотрібною? Можливо, прийняття транзакцій є найбільш доступною жертвою.

Варіант 2: Використовуйте чергу

Якщо є достатня впевненість у тому, що інша послуга матиме успіх у тому, що ми хочемо, ми можемо викликати її через певну форму черги. Елемент із черги не буде зібрано до пізніше, але ми можемо переконатися, що цей предмет у черзі .

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

Begin transaction
Insert entity
Insert e-mail
Commit transaction

Очевидним недоліком є ​​те, що декілька мікросервісів потребують доступу до однієї таблиці.

Варіант 3: Зробіть зовнішню роботу останньою, безпосередньо перед завершенням транзакції

Цей підхід ґрунтується на припущенні, що скоєння угоди малоймовірне не вдасться.

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

Якщо запити не вдається, зовнішній виклик ще не відбувся. Якщо зовнішній виклик не працює, транзакція ніколи не здійснюється.

Цей підхід має обмеження того, що ми можемо зробити лише один зовнішній дзвінок, і це потрібно зробити останньою (тобто ми не можемо використовувати його результат у наших запитах).

Варіант 4: Створення речей у стані очікування

Як розміщено тут , ми можемо з декількох мікросервісів створювати різні компоненти, кожен у відкладеному стані, без транзакцій.

Будь-яка перевірка виконується, але в остаточному стані нічого не створюється. Після того, як все було успішно створено, кожен компонент активується. Зазвичай ця операція настільки проста, і шанси на те, що щось піде не так, настільки малі, що ми навіть можемо вважати за краще робити активацію без транзакцій.

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

Варіант 5: Нехай мікросервіс поділиться своїм запитом

Жоден з інших варіантів це не робить для вас? Тоді давайте неортодоксально .

Залежно від компанії, це може бути неприйнятним. Я знаю. Це неортодоксально. Якщо це не прийнятно, перейдіть іншим маршрутом. Але якщо це відповідає вашій ситуації, це вирішує проблему просто та потужно. Це може бути просто найбільш прийнятний компроміс.

Існує спосіб перетворити запити з декількох мікросервісів у просту, єдину транзакцію бази даних.

Поверніть запит, а не виконайте його.

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

Мережевий, кожен мікросервіс повинен мати доступ до кожної бази даних. Майте це на увазі, також стосовно майбутнього масштабування.

Якщо бази даних, що беруть участь в транзакції, знаходяться на одному сервері, це буде звичайною транзакцією. Якщо вони знаходяться на різних серверах, це буде розподілена транзакція. Код однаковий незалежно.

Ми отримуємо запит, включаючи його тип з'єднання, його параметри та рядок з'єднання. Ми можемо перетворити його в акуратний виконуваний клас Command, зберігаючи потік читабельним: виклик мікросервісу призводить до команди, яку ми виконуємо, як частина нашої транзакції.

Рядок з'єднання - це те, що дає нам початкова мікросервіс, тому для всіх намірів і цілей запит як і раніше вважається виконаним цією мікросервісом. Ми просто фізично прокладаємо це через мікросервіс клієнта. Це має значення? Ну, це дозволяє нам помістити його в ту саму транзакцію з іншим запитом.

Якщо компроміс прийнятний, такий підхід дає нам пряму транзакційність монолітного додатку в архітектурі мікросервісу.


0

Я б почав з розкладу проблемного простору - визначення ваших службових меж . При правильному виконанні вам ніколи не потрібно буде здійснювати трансакції між службами.

У різних служб є свої дані, поведінка, мотиваційні сили, уряд, правила ведення бізнесу тощо. Добре початок - перерахувати, які можливості на високому рівні має ваше підприємство. Наприклад, маркетинг, продаж, облік, підтримка. Інший вихідний пункт - організаційна структура, але пам’ятайте, що існує застереження - з деяких причин (наприклад, політичних) це може бути не оптимальна схема декомпозиції бізнесу. Більш суворий підхід - це аналіз ланцюга вартості . Пам'ятайте, що у ваші послуги можуть входити і люди, це не суто програмне забезпечення. Служби повинні спілкуватися між собою через події .

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

Якщо ви вважаєте, що ваш домен вимагає послідовності спочатку, подумайте ще раз. Жодна з великих та критично важливих систем не будується з урахуванням цього. Всі вони розподіляються і з часом узгоджуються. Перевірте класичний папір Пат Гелланд .

Ось кілька практичних порад щодо побудови розподіленої системи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.