распределенные транзакции в микросервисной архитектуре, как обрабатывать тайм-ауты и неудачные коммиты

Допустим, у вас есть служба A, которая является частью большой микросервисной архитектуры, где эти службы взаимодействуют друг с другом либо через REST API, либо через обмен сообщениями, в котором задействован какой-либо брокер (RabbitMQ). Служба A предоставляет конечную точку REST, которая должна взаимодействовать с какой-либо сторонней службой (то есть со службой, которой нет в нашей архитектуре), и создавать там какие-то вещи, когда служба A получает ответ от третьей стороны о том, что там все прошло хорошо, он должен сохраняться некоторые данные из этого ответа в его собственной базе данных.

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

  1. Создание на сторонней стороне прошло успешно, но запись в БД в службе A завершилась неудачно. Это приведет к несогласованному состоянию, вы создали что-то на сторонней стороне, но у вас нет необходимых данных об этом в вашей собственной базе данных.

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

Проблема номер 1. может быть решена с помощью любого механизма повтора, который может повторять вызов БД любое количество раз. Проблема с этим подходом заключается в том, что если экземпляр службы A, который повторяет вызов БД, внезапно отключается.

Предположительно, лучшим подходом было бы, чтобы служба после успешного создания на сторонней стороне публиковала сообщение RabbitMQ об успешном создании. Эта служба будет прослушивать это сообщение и может выполнять вызов БД при получении сообщения. Имея хороший механизм повтора и пользуясь сообщениями ACKing, можно было бы решить проблему What if service instance goes down suddenly. Итак, в этом решении служба является издателем и потребителем своих собственных сообщений. Есть идея лучше? Это решение также представит конечную согласованность, поскольку вызывающий API (парень, который вызывает конечную точку службы A) получит ответ сразу после успешного создания на сторонней стороне, но до того, как что-либо сохраняется в базе данных службы (что на самом деле нужно клиенту API)

Что насчет проблемы с тайм-аутом? Как в этом случае обрабатывать тайм-ауты от третьей стороны?. Я не вижу ничего лучше, чем запускать вызовы GET, чтобы проверить, создали ли они что-то. Опять же, вызов GET может завершиться ошибкой, но его можно повторять до тех пор, пока он не завершится успешно. Здесь также маргинальный вариант использования - это то, что, если служба выйдет из строя во время выдачи вызовов GET.


person Srle    schedule 06.04.2017    source источник
comment
Вам следует подумать об отказе от распределенных транзакций и двухфазных фиксаций. Это кошмар для настройки и обработки в облачной среде или когда задействован сторонний код / ​​инфраструктура. Альтернативой 2PC является конечная согласованность и компенсационные транзакции.   -  person Bishoy    schedule 06.04.2017
comment
@Bishoy, как я использую или предлагаю здесь 2PC? Как я уже сказал, по крайней мере, на мой взгляд, здесь я предлагаю асинхронную обработку с участием брокера и возможную согласованность.   -  person Srle    schedule 06.04.2017


Ответы (1)


Правильно настроить отказоустойчивость непросто. Я помню, что для стека netflix они реализовали специальный модуль: hystrix. Может быть, это вам поможет.

person Oswin Noetzelmann    schedule 07.04.2017
comment
С NServiceBus я бы решил этот сценарий, используя функцию буксировки под названием saga docs.particular.net/nservicebus/sagas и исходящие docs.particular.net/nservicebus/outbox - person Sean Farmar; 09.04.2017