Как правильно построить работу с очередями/топиками брокера в транзакционной системе?
Добрый день!
Меня с моей командой схантили в новый проект, сханитили за наш успешный, четырехлетний опыт разработки для одного банка с нуля платформы с микросервисной архитектурой, в рамках которой функционирует: интернет-банкинг, электронные деньги и платежная система за разные услуги сынтегрированная с несколькими экваирингами.
По сути сейчас от нас хотят того же самого, что мы делали для банка, но есть одно но - требованиями нам заложили межсервисные вызовы через Apache Kafka. Текущий проект намного больше прошлого, транзакционная система (ответственность моей команды) составляет, примерно 1/6 от всего проекта. Другими словами у нас трудится 6 команд и транпортный уровень между сервсиами реализуется через Kafka. У нас есть опыт межсервисных вызовов только посредством HTTP. Я ничего не имею против Kafka, в целом я считаю, что это даже правильнее чем HTTP, но у нас совсем нет опыта работы ни с какми брокерами очередей вообще.
Предыстория проблемы вообще такая: сначала нам дали требования использовать rabbitMQ, который мы также не знали. Было интересно - почитали, применили, работает. Через два месяца, в команду взяли эксперта, который какими-то аргументами убедил архитектора поменять транспортный уровень на Kafka. Ну чтож, почитали, потратили неделю на переделку - работает, ничего принципиально не изменилось. Прошло еще 4 месяца и тот эксперт уволился. Фактически, сумарный штат 6-ти команд около 35 человек и никто не имеет опыта работы с брокерами сообщений.
На тестовых серверх все работает, но я боюсь выхода в продакшн. Грубо говоря транзакционная система реализована по паттерну SAGA основанной на оркестровке, т.е. есть главный сервис-оркестратор transactions и подчиненные ему сервисы, которые делятся на две группы:
1 за что производится оплата:
1.1 оплата заказа с нашего маркетплейса;
1.2 оплата заказа партнерских услуг;
1.3 оплата прочих услуг интегрированных через агрегаторы услуг, таких как QIWI;
2 чем производится оплата:
2.1 карта;
2.2 кредитом в одном из банков;
2.3 бонусами;
2.4 частично бонусами.
Каждый подпункт - это отдельный сервис или группа сервисов, на данный момент суммарно их 15 и еще несколько разработке. Сервис-оркестратор определяет "за что" и "чем" происходит оплата и делегирует ведомым сервисам. Если бы это был HTTP, то это были бы два-три обычных HTTP-запроса и все. Как же быть с Kafka? Сейчас по аналогии с HTTP у кажого свой топик на который подписан каждый сервис и в сервис-оркестратор просто в зависимости от типа платежа публикует событие в разные очереди. Пока что работает, но не лишен ли этот способ недостатков? Сейчас у меня есть мысль, о том, что я мог бы сделать одну единую очередь и подписать на нее все сервисы, к сообщению же просто добавил бы тип транзакции по которому каждый подписанный сервис понимал бы для него это сообщение или нет.
Какой из вариантов правильнее? Может оба плохие, как нужно делать?
а как транзакционно делается запись в БД и взаимодействие с 3ми АПИ (например апи платежек)? интересует кейс, когда где-то что-то зафейлилось как это откатить?
Даша Циклаури, Я должен был в посте дать уточнение, что под транзакцией я понимаю само движение денег, а не механизм БД.
Если коротко, то ответ на ваш вопрос - никак. Система имеет выскоий риск оказаться в неконсистентном состоянии и это происходит достаточно часто, но это нормально, в целом есть достаточно много корректироваок на уровне второй линии поддержки, типа ручного допроведения транзакций.
Если подробно как-то так:
Сервис-координатор имеет несколько типов контрактов типа: ОплатаЗаУслугуКартой, ОплатаЗаУслугуБонусами, ОплатаЗаУслугуКартойИБонусами и т.д. Каждый контракт состоит из предопределенного типа шагов, которые исполняются в строгом порядке. Если отбросить шаги в которых происходят всякие проверки на лимиты или проверки безопасности, то для контракта с типом ОплатаЗаУслугуКартой само непосредственное движение денег происходит четырьмя шагами:
1 АвторизируйПлатежУПоставщикаУслуги;
2 АвторизируйПлатежУЭкваиринга;
3 ПодтвердиПлатежУПоставщикаУслуги;
4 ПодтвердиПлатежУЭкваиринга;
Шаг авторизации- это получение гарантии от партнерских систем о том, что они готовы сейчас провести платеж. Если ошибка произошла на этом шаге, то это значит, что дальнейшие шаги исполнять смысла нет, деньги никуда не двинулись и ничего возвращать не надо;
Шаг подтверждения - это как раз приказ об исоплнении транзакции. Ошибки на этом шаге могут привести систему в неконсистентное состояние. Независимо от полученного ответа тут начинается полинг терминального-статуса транзакции в партнерской системе. Терминальный статус - это статус говорящий, что трарнзакция либо однозначно успешна, либо однозначно неуспешна. Если текущий шаг подтверждения успешен, то запускается следущее подтверждение. Если подтвеждение было неуспешным, то происходит отмена всех подтвержденных ранее шагов.
В целом описанная выше схема надежна, точнее она стала надежной к концу второго года работы в банке. Почти всегда транзакция аннулировалась минута-в-минуту после получения ошибки. Иногда дольше, иногда без участия человека - никак. У нас появилась отчетность, на которую был настроен монторинг. Также все все поставщики присылали нам свои отчеты о прошедших через них деньгах. Большая часть проблем анализировалась и решалась автоматизированно, либо полуавтоматихировано, т.е. под каждый более-менее частый кейс созавался инструмент (скрипт и кнопка запускающая его). Оператору второй линии поддержки просто нужно было принять решение запускать его или нет.
Zhainar, Если сократить мой пост до одного предложения, то вопрос будет таким: "Транзакцую исполняемую 5-7 сервисами правильно публиковать через один топик или для каждого сервиса должен быть свой топик?". Диаграму вызовов рисовать смысла не вижу, т.к. она будет одинаковой в обоих случаях.
Zhainar, Мой вопрос скорее не про то стоит ли создавать отдельный канал для каждого сервиса. А о том стоит ли создавать отдельный канал на одно и тоже действие. Я вижу риск в том, что сервис-координатор, публикуя события в разные топики и получая ответы из разных топиков может получить их не в том порядке. Хотя... пока я описывал это опасение, мне пришло в голову, что я могу публикоать в разные очереди, а ожидать ответ в одну. Наверное, это решит проблемы.
Прошло еще 4 месяца и тот эксперт уволился. Фактически, сумарный штат 6-ти команд около 35 человек и никто не имеет опыта работы с брокерами сообщений.
А нового, со знанием Kafka найти не получается?
Система имеет выскоий риск оказаться в неконсистентном состоянии и это происходит достаточно часто, но это нормально, в целом есть достаточно много корректироваок на уровне второй линии поддержки, типа ручного допроведения транзакций.
ИМХО это не нормально, что если таких состояний окажется очень много, что тогда нанимать тысячи людей на вторую линию?
> А нового, со знанием Kafka найти не получается?
Не владею информацией, в любом случае его нет
> ИМХО это не нормально, что если таких состояний окажется очень много, что тогда нанимать тысячи людей на вторую линию?
Будем решать по мере возникновения. За три года работы в банке ни разу небыло необходимости иметь более троих человек одномоментно.
Решил сам ответить на ворос. Управление сервисами построено по паттерну SAGA основаному на оркестрации, сервис реализующий сагу имеет ИНДИВИДУАЛЬНЫЙ командный канал к каждому сервису-участнику саги, и ОБЩИЙ канал с ответами от них же.
Спасибо всем учавствовшим в обсуждении.
Zhainar
Спасибо за отсылку к книге Криса Ричардсона, я ее прочел всю. Было очень полезно, стал лучше понимать паттерн SAGA, ответ и помощь нашел в книге.
SirotaKazansky
То, что я называл "частым нарушением согласованности" по Крису Ричардсону называется "отложенной согласованностью". Это состояние, когда каждый сервис сам по себе находится в согласованном состоянии, но система в целом может быть в процессе установления этой согласованности. Специалист второй линии поддержки, о котором я говорил ранее, в случае возникновения ситуцаии когда система по какой-то причине не выходит из отложеной согласованости, после анализа этой ситуации просто перезапускает конкретный шаг саги или же иницирует ее отмену запуская компенсирующие транзакции.