В разработке программного обеспечения микросервисная архитектура приобрела популярность в последние годы благодаря своей способности повышать масштабируемость и модульность. Однако при разделении базы данных мы иногда рискуем потерять некоторые из основных гарантий, которые мы получаем с транзакциями базы данных; в частности, атомарность транзакций. В этом сообщении блога мы рассмотрим опасности внедрения микросервисной архитектуры без учета транзакций, одну из возможных моделей восстановления атомарности межсервисных транзакций, а также компромиссы, связанные с производительностью и ремонтопригодностью приложения.

Любой, кто прошел вводный курс по базам данных, знает о принципе ACID. Однако ACID применим не только к базам данных; этот принцип также следует считать священным для микросервисов, несмотря на то, что вы можете в конечном итоге разрушить свою базу данных.

Хотя базовая база данных в каждой микрослужбе по-прежнему будет поддерживать согласованность, изоляцию и устойчивость, одной из опасностей, с которыми можно столкнуться при внедрении архитектуры микрослужбы, является нарушение принципа атомарности транзакций (это A в КИСЛОТЕ). Это принцип, который требует, чтобы транзакция была атомарной единицей работы, которая либо завершена полностью, либо не завершена вообще. В распределенной системе может быть сложно обеспечить соблюдение принципала, особенно когда в одной транзакции задействовано несколько сервисов.

Например, представьте, что пользователь хочет совершить покупку на веб-сайте электронной коммерции. Микросервисная архитектура веб-сайта включает в себя сервис для обработки платежей и сервис для управления запасами. Если услуги не скоординированы должным образом, может возникнуть состояние гонки, что приведет к обновлению запасов до обработки платежа. Это может привести к ситуации, когда с учетной записи пользователя взимается плата за продукт, которого больше нет на складе, что нарушает принцип атомарности транзакций.

Важно отметить, что эта опасность не является тривиальной. Большинство разработчиков воспринимают ACID как должное, потому что мы (по сути) получаем его бесплатно, когда полагаемся на архитектуру, в которой все происходит в одной базе данных. Таким образом, это ловушка, в которую легко попасть, если ваша цель — просто разбить большое монолитное приложение на микросервисы.

Чтобы устранить эту опасность, можно использовать протокол двухэтапная фиксация (2PC) для обеспечения атомарности транзакций в распределенной системе. Протокол 2PC включает в себя две фазы: фазу подготовки и фазу фиксации. На этапе подготовки все службы, участвующие в транзакции, готовятся к фиксации транзакции, обеспечивая ее безопасность. На этапе фиксации, если все службы согласны с тем, что фиксация транзакции безопасна, транзакция фиксируется. Если какая-либо служба определяет, что фиксация транзакции небезопасна, транзакция откатывается.

Хотя протокол 2PC может обеспечить атомарность транзакций, он сопряжен с компромиссом в производительности. Протокол 2PC требует вдвое большего количества сетевых запросов для связи и координации, что может увеличить задержку и снизить пропускную способность.

Реализация протокола двухэтапной фиксации в архитектуре микрослужбы требует дополнительных инженерных издержек, чтобы гарантировать, что бизнес-логика каждой микрослужбы может участвовать в протоколе. Логика каждого микросервиса должна быть изменена, чтобы включить необходимый код для участия в протоколе двухэтапной фиксации, что может быть трудоемкой и сложной задачей со многими собственными ловушками и опасностями. Кроме того, реализация протокола двухэтапной фиксации может привести к усложнению API каждой микрослужбы, поскольку управление координацией между отказоустойчивыми службами может быть затруднено.

Например, если клиентскому микросервису необходимо перезагрузиться в середине транзакции (после того, как он ответил на запрос подготовки, но до фиксации) и не сохраняет в памяти свое состояние, запрашивающий сервис должен иметь возможность обрабатывать в этом случае и повторно отправить запрос на фиксацию. Кроме того, запрашивающая служба нуждается в некотором уровне отказоустойчивости для обработки редкого случая, когда и клиент, и запрашивающая служба терпят неудачу в середине транзакции после этапа подготовки.

По этим причинам решение о том, когда использовать микросервисы (или каким должен быть микросервис), должно быть хорошо продумано и серьезно принято.

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