На главную

Часть III. Проблемы и решения

Вас не удивило, что проблема 70-х — высокая сцепленность кода — дожила до 2010-го и способствовала изобретению микросервисов? Если так, вы не удивитесь и, узнав, что микросервисы также её не решили. Сегодня индустрия относится к микросервисам скептически. За последние десять лет мы поняли, что они не стали панацеей. Архитекторы в мире IT — это не учёные и даже не художники. Это шаманы. Удачно разбить систему на несцепленные части было сложно в 70-е, сложно и сейчас.

Однако, микросервисы привносят проблемы, которых не было в монолитных приложениях.

Проблемы

Отказ от транзакций

В 80–90 годы на платформе x86 под базами данных понимали dBase и Paradox. В этих СУБД транзакций не было. В середине 90-х появились сервера SQL и программисты вздохнули свободно. При всей сложности прикладной разработки, любой программист БД твёрдо уверен в своих транзакциях. Это действительно фундамент. И от этого фундамента вам придётся отказаться, потому что микросервисы не должны зависеть друг от друга, и, конечно, не должны заглядывать в чужие базы.

Одним из решений в этой ситуации будет отказ от транзакций. Стоят ли микросервисы такой жертвы?

Сеть

Вызывая метод класса, вы точно знаете, что он будет выполнен и вернёт результат. Отправляя HTTP-запрос, вы не можете быть уверены ни в чём. Сеть непредсказуема и вам приходится это учитывать.

Может сломаться оборудование. Может оборваться провод. Может зависнуть один из ключевых сервисов — DNS или балансировщик нагрузки. Иногда вам будет сложно найти точку отказа. Компоненты, которые используют сломаный сервис, начнут записывать ошибки в свои логи — и, возможно, вам предстоит проверить их все.

Версионирование

C# — это язык со статической, довольно строгой типизацией. Мы, как разработчики, уверены в целостности наших проектов. Если в одном из методов появится новый параметр, наша программа не скомпилируется, пока мы не добавим значение во все места вызова. Даже если над проектом работают независимые команды, компилятор обеспечивает соответствие контрактов. В мире микросервисов это не так. Хотя микросервисы не вызывают друг друга, они могут обмениваться сообщениями. Представьте, что в одном из микросервисов изменился формат сообщений. Мы получим ошибку, которую трудно обнаружить.

Нам приходится обеспечивать соответствие версий микросервисов друг другу. И не всегда эта работа может быть автоматизирована.

Сцепленность

Реальный мир непросто разделить на независимые контексты. Границы контекстов размыты — непонятно, где заканчивается один микросервис и начинается другой. Сцепленность всей системы не снижается, и в конце разработки вы получаете тот же монолит, но уже без транзакций и статической типизации.

Похоже, мы в тупике. С одной стороны у нас монолит, который сложно поддерживать, а с другой — микросервисы, в которых нет ни предсказуемости, ни транзакций.

Как быть? Существует ли срединный путь?

Решения

Сразу скажу, что готовых надёжных решений не существует. Есть подходы к решениям, которые вы можете примерить на свою ситуацию.

Транзакции

Транзакциям посвящена седьмая глава книги Высоконагруженные приложения Мартина Клеппмана.

Подсмотрим несколько решений.

Сцепленность

Обсудим способы обеспечения целостности на примере интернет-магазина.

Одной из важнейших сущностей в магазине является Заказ.

Оформление заказа происходит за несколько шагов. Сначала покупатель добавляет товары в корзину, потом выбирает способ доставки, потом оплачивает покупку, и так далее, пока курьер не доставит заказ покупателю.

Весь путь от начала и до конца называется бизнес-процессом. Мы могли бы реализовать весь бизнес-процесс в рамках одной программы, но это как раз и ведёт к высокой сцепленности.

Некоторые части нашего магазина будут меняться очень редко, а некоторые — постоянно. Например, способы доставки и способы оплаты — очень нестабильная часть программы, точно так же, как и скидки.

Неизменными в нашем случае является сам процесс и отдельные его шаги. Добавление товаров в корзину — неизменный шаг, который вряд ли придётся переписывать.

Неизменные части мы размещаем в основном модуле программы (в центральном сервисе, в разделяемом ядре), а изменяемые — в микросервисах.

Теперь мы можем распределить работу над сайтом между несколькими командами. Кто-то будет развивать ядро, кто-то — микросервис с выбором доставки, а кто-то — микросервис со скидками.