Как перевести банковский продукт в realtime

Частая история: приходит пуш-уведомление от банка, что вам одобрено классное предложение. Причём это уже десятое сообщение за пару месяцев и никакое оно для вас не важное и не классное.
Тизер: да, мы будем говорить о цифровом маркетинге в реальном времени от финансовых организаций — кредит, ипотека, депозит. Научим, как сделать так, чтоб клиент получал своевременные и выгодные предложения, а не спам.
Привет, Хабр! Это Владимир Ловцов и Владимир Аврамов из «ИТ-холдинга Т1». И мы расскажем о формировании офферов финансовых продуктов в realtime. Поехали!
Требования клиента к продукту
Перед разговором о любом продукте стоит задуматься, кто же наш клиент. Ведь без него ни один продукт не может начать свой жизненный цикл. В этой статье по мотивам доклада для конференции Saint Hihgload++ расскажем о продуктах и маркетинговых предложениях от финансовых организаций — готовых решениях, ради которых не нужно идти в отделение.
Мы в большинстве своём хотим получать от финансовых организаций такие уведомления:
-
своевременные;
-
выгодные;
-
готовые решения, не вынуждающие идти в отделение;
-
с минимумом действий — достаточно зайти в личный кабинет и нажать на кнопку.
То есть мы хотим получать финансовое предложение от любой организации только тогда, когда нам это нужно. Если что-то случилось или мы чем-то заинтересовались, тогда хорошо, что банк это предложил! А если ещё и подключить его можно, нажав кнопку в личном кабинете — вообще красота.
Описание продукта
Наша команда от ИТ-холдинга Т1 отвечает за реализацию проектов заказчиков. В их числе и бизнес-процесс для выдачи маркетинговых предложений в realtime. Изначально планировали, что будем работать с десятками, сотнями тысяч запросов в секунду (RPS) и установим планку с возможностью масштабирования до 1 миллиона запросов. При этом могут быть ограничения на той или иной стадии MVP.
Особенности продукта:
-
событийная архитектура;
-
формируем предложения по продуктам банка для клиентов;
-
всегда актуальные предложения.
Преимущество решения в том, что мы обрабатываем события потоково. На вход нам приходят события, касающиеся клиентов. На выходе мы отправляем так называемые маркетинговые или предодобренные предложения. Это не просто реклама, а предложения конкретному клиенту, которые его устроят, и он их может оформить по клику.
Преимущество в том, что мы не храним большие объёмы данных со всеми вытекающими последствиями. Вместо этого обрабатываем данные онлайн и формируем предложение.
При этом в наших процессах полнейший асинхрон, множество микросервисов, разработанных за очень короткий промежуток времени. И нас пока всё устраивает. Ведь мы выполняем главную функцию бизнеса — заработать деньги.
С чего мы начали

В октябре 2023 года предложили создать и возглавить новый проект в энтерпрайзе. Энтепрайз — это всегда сложно. А ещё это всегда что-то большое и надо суметь встроиться в энтерпрайз-архитектуру, пройти через все процессы и пайплайны.
За два месяца сформировали костяк будущей команды с нуля, хотя в октябре ещё не было никого. Костяк — это два системных аналитика, один супер-тестировщик и три разработчика.
В конце декабря — середине января мы уже представили первый работающий технический прототип. Архитектуру наметили в октябре, а коллеги-разработчики помогли реализовать. И в феврале мы уже показали первый бизнес-прототип, который можно было использовать на продакшене.
К марту бизнес-требования существенно изменились. Это грозило стать проблемой, но ребята из разработки и аналитики не подвели. В течение месяца мы уже вывели новую версию продукта с учётом новых вводных. Сначала на 13, а в июне — на 15 микросервисов.
В апреле, мае, июне уже работали в продакшен и получали множество багов. Это было непросто.
Место в корпоративной архитектуре

Корпоративная архитектура в энтерпрайзе Т1 — большая и сложная. Например, шина протокола — это не одна система, а порой тысячи или несколько тысяч систем, с которыми нужно взаимодействовать, не порушив весь процесс. Ведь аварии, инциденты и все проблемы валятся на голову руководителей проекта.
В корпоративную архитектуру мы встроились не без сложностей, но успешно. Мы взаимодействуем с огромной машиной кредитного конвейера, с множеством мастер-систем по обогащению нас данными. И точно не собираемся ничего держать у себя. Ведь нам надо быть максимально быстрыми, оперативными, масштабируемыми и обеспечивать realtime.
Мы отслеживаем события по клиенту, фильтруем десятки потоков и всё это балансируем. Затем наши 15 микросервисов обрабатывают эти данные и формируют предложения. После этого выдаём пачку предложений и приоритизируем их, потому что клиенту точно не нужно так много. Ему нужно что-то одно, реально актуальное и важное. Мы заточены на ответное предложение, то есть, когда клиент чего-то хочет или о чём-то задумывается, мы ему это даём.
Архитектура realtime-конфигуратора

Сердце нашего решения — это расчёт, который крутится на Flink-приложениях в Калькуляторе. (на картинке выше — белочка слева). А вспомогательный модуль, который фильтрует, выбирает по алгоритму и отдаёт их второй белочке с надписью «калькулятор» — это Flink фильтратор. Он формирует предложения для клиентов — наиболее выгодные и интересные, чтобы они были счастливы и довольны.
В качестве резидентной базы данных мы используем Tarantool. К ней обращаются все участники системы. У нас есть 9 support-сервисов на базе Spring Boot, которые занимаются аудитом и другими задачами, необходимыми другим сотрудникам. Работает это всё по шине сообщений Kafka, через которую мы взаимодействуем с внешними системами.
На схеме всё выглядит просто. Но спроектировать это было совсем не просто — ведь нужно было, чтобы система сто раз выдержала всевозможные переработки и изменения бизнес-сценариев.
Почему выбрали Flink
Приоритетом было оперативно разработать продукт в очень сжатые сроки. У нас не было огромного количества времени на разработку — ни года, ни двух. Нужно было за пару месяцев представить уже готовый прототип решения и доработать его буквально через шесть месяцев, чтобы оно оказалось полностью в продакшене и обрабатывало огромные массивы данных.
Наши требования:
-
многопоточность — млн RPS;
-
realtime;
-
масштабируемость — от 1 до млн RPS;
-
всё из коробки — бери и делай!
-
оптимизация — управление ресурсами;
-
API и библиотеки — удобство;
-
динамическое управление ресурсами.
То есть всё должно быть готово «из коробки». Желательно, чтобы всё дружило и с железом, и с виртуализацией. Мы должны были обеспечить такую многопоточность, которая позволит масштабироваться до десятков, сотен, тысяч RPS и дальше, возможно, до миллиона. Это нужно было сразу заложить в архитектуру. Всё должно было быть готово — библиотеки уже существовать для интеграции с самыми простыми технологиями типа Kafka, Postgres, и дружить с основными языками (Java, Scala и пр.).
Flink и страхи
Использование Flink сопряжено со следующими обоснованными опасениями:
-
нет реальных специалистов на нашем рынке или их очень мало;
-
очень маленькое комьюнити;
-
много теоретиков, мало практиков;
-
высокая оплата труда узкоспециализированным разработчикам;
-
нехватка знаний и опыта;
-
проблема с соблюдением сроков;
-
интеграция с существующей инфраструктурой.
Нас эти страхи тоже коснулись. Мы затаскивали себе в проекты множество технологий, которые знаем лишь вскользь, но полноценно с ними ни разу не работали. Имели дело с мультимодельными базами данных, BigData, но никогда с жёстким realtime под капотом.
На нашем российском рынке немногие знают Flink, у него очень маленькое комьюнити. Поэтому одного человека, который очень хорошо его знает, забрали на проект сразу.
Также был страх, что мы не сможем выкатить продукт вовремя, уложиться в redline и deadline. Сомневались, заведётся ли вся эта машина в итоге.
Зоопарк языков: Java, Scala и Lua
Менеджеры, техлиды или даже ребята из разработки скажут — зачем вам столько языков, почему не один язык? Ведь один язык — это удобно, легко менять людей и т.д.
На самом деле, зоопарк у нас не очень большой, бывает и больше:
-
Java —– классика для банка, используем Spring и его подпроекты, так как всё есть под капотом.
-
Scala — классика для BigData, Flink-приложения. Хотя, в принципе, на Flink можно писать и на Java — никто не мешает. Они живут в синергии, как JVM-based языки.
-
Lua —– язык для Tarantool. Это скриптовый язык со всеми вытекающими, но от него никуда не денешься. По большому счёту, если выбрать Redis, то там тоже Lua.
-
SQL — так как было сказано про PostGreSQL жить без SQL не может.
Почему выбрали Tarantool
Tarantool мы переписывали не один раз. Выбирали резидентное хранилище, как водится, всем миром, но выбирали между Redis и Tarantool.
В Tarantool привлекли:
-
высокая производительность — до млн RPS;
-
масштабируемость — до млн RPS;
-
гибкость хранения данных;
-
скорость обработки — до млн RPS;
-
не только кэш, а ещё и сервер приложений;
-
есть поддержка и обучение.
Бонусом к этому шло построение модели данных, схожее с любимыми реляционными базами данных. Там даже можно налагать внешний ключ, чтобы было ещё лучше. Поэтому, подумав, взяли Tarantool.
Tarantool и арахнофобия
Страхи были и насчёт Tarantool 2.0, но в будущем хотим посмотреть на 3.0:
-
отсутствие экспертизы в команде;
-
ограниченная документация — декларативная;
-
страх новой технологии и выход из мира Java;
-
молодая технология с вероятными рисками — дефекты, мало библиотек.
Главная причина арахнофобии — не широко используемая технология, немного другой мир. Плюс к этому ограниченность комьюнити в сравнении с тем же Redis. Ведь технология не очень используемая, хотя и не очень молодая. Тем не менее она набирает популярность и мы думаем, всё у неё впереди.
К чему нужно быть готовым выбрав наш путь
Если делаешь интересный проект и нужно уложиться в горящие сроки, не всегда первичная концепция будет оставаться такой же. Придётся перерабатывать её под команду, потому что в команде будет не хватать каких-то скиллов.
Мы сразу решили, к чему надо точно быть готовым, если выбираем идти по такому процессу:
-
к мультиязычности проекта;
-
к Lua, если берёте Tarantool;
-
к жизни на Linux и иметь в этом опыт;
-
к изучению API Tarantool;
-
к отличиям CI/CD от классики;
-
к появлению багов в официальных версиях ПО;
-
к перестройке архитектуры по 100 раз.
Конечно, мы отловили парочку багов. И не только в своём коде, а в реальных официальных версиях ПО и OpenSource и, возможно, в уже готовой разработке.
Как строится приложение на Tarantool и его милые особенности
Жизненный цикл Tarantool-приложения не сильно отличается от Java-приложения — мы его билдим, запускаем тесты, передаём на деплой, деплоим в разные контура.
Фаза планирования:
-
определение требований;
-
архитектурное проектирование;
-
выбор инструментов и технологий.
Фаза инициализации:
-
настройка окружения;
-
создание проекта.
Фаза разработки:
-
моделирование данных;
-
написание кода;
-
интеграция с внешними системами.
Фаза тестирования:
-
Unit-тестирование;
-
интеграционное тестирование.
Фаза оптимизации:
-
профилирование и оптимизация.
Фаза деплоя:
-
настройка производственной среды;
-
деплой приложения;
-
мониторинг и логирование.
Пост-релизные действия:
-
поддержка и обслуживание;
-
отзывчивость на фидбэк.
Проблема в том, что Tarantool строится на Lua, а там свой сборщик и экосистема. Плюс всё это работает на Linux. Если у вас настроен пайплайн и сборка на Java, вашему DevOps-инженеру придётся настраивать всё заново — ведь для Tarantool он будет отличаться, а ещё для каждой версии Tarantool — свой пайплайн. В принципе, не страшно, но требует дополнительных усилий.
Tarantool — это полноценный сервер приложений. Он позволяет писать приложение. Тут нужно понимать, какого баланса вы хотите достичь, какую часть логики перенести в Tarantool и хотите ли вообще, а какую часть решаете оставить на внешних сервисах.
У нас, например, вначале часть логики по сохранению в Postgres и чтению из Kafka была полностью реализована на Tarantool. Но в силу особенностей контуров пришлось переносить это во внешние Java-сервисы. Этот баланс нужно заранее продумать.
Наш спринт по контурам разработки
И вот нам предстояло решить важные вопросы:
-
Откуда взять сборку в защищённом контуре?
-
Как правильно развёртывать?
-
Как писать на Lua?
-
Tarantool не доехал в том виде, в котором реализовали на Dev-стенде.
-
Пришлось срочно переделывать микросервисы.
Начинали спринт на самом маленьком контуре Dev и столкнулись с вопросами, где брать разработку и какую, и как подружить её с Enterprise-архитектурой. А ещё, как встроить в текущий пайплайн.
В итоге, когда стали писать на Tarantool, наша логика начала отваливаться от него по кускам. Мы ошиблись, развернув разные кластеры между контурами. Из-за этого пришлось отключить часть логики и перенести её на Spring. И снова срочно перепиливать микросервисы, допиливать их под текущую архитектуру в рамках интеграции Kafka. Причём в одном месте была одна версия решения, в другой — другая, а ещё в придачу разные шины. Чтобы всё это настроить, везде подключиться, нужно было балансировать версии библиотек. Не самое весёлое занятие.
И вот мы вроде всё разработали, выкатили первый бизнес-прототип, проверили досконально все вычисления. Всё должно было работать нормально, но не работало из-за ошибки в расчётах. Было непонятно, что мы упустили.
Первый баг и его патч
Это был первый баг при сложном запросе в Tarantool. Запрос содержал много условий, и при изменении их порядка мы получали разные результаты. Сначала решили, что у нас кривые руки, и начали их исправлять код. В итоге пошли в красный чат, задали вопросы, получили ответы вновь разобраться с руками. Мы продолжили дебагинг, и выяснилось, что ошибка где-то в модуле CRUD. CRUD — модуль Tarantool, который позволяет искать по шардам в кластере. После этого мы пригласили разработчика, который любезно согласился помочь с отладкой. Полдня дебага привели к тому, что мы нашли банальную ошибку — оптимизатор Tarantool, исполняя запрос, думал, что на все поля наложены отсортированные индексы, и отсекал выборку, возвращая ноль в начале.
Этот баг был далеко не единственный. В процессе мы отыскали ещё несколько глобальных. Это был интересный опыт, который приобрела вся команда: находишь баг в реальном продукте и в том самом модуле, который используют многие другие команды. И ни у кого не возникает вопросов.
Чего достигли, и здравствуй, продакшен!
По итогу, буквально с октября, то есть всего за 2,5−3 месяца реальной разработки, мы вышли в продакшен.
Реализовали 15 сервисов:
-
5 на Flink, 4 из них фильтрующих, и одно приложение — мозг, который производит все самые высоконагруженные расчёты;
-
9 на Java;
-
1 TT;
-
каждый микросервис отправляет в Tarantool до 100 запросов в единицу времени на поток, и Tarantool великолепно с этим справляется.
Мы изначально рассчитывали, что можем обрабатывать на Tarantool порядка 1 миллиона внутренних RPC. Tarantool справляется с огромным числом запросов и пересчётом внушительной очереди.
-
Flink-приложения не общаются напрямую с ESB через Java Spring middleware;
-
основная логика на Flink-приложениях побочная – Java.
Основную бизнес-логику мы положили во Flink, а всё, что касается не бизнес-логики — на Java. Это оказалось правильным решением. Когда часть микросервисов вообще без бизнес-логики, она не страдает. Ведь даже если что-то не успели дотестировать или обнаружили баг, весь бизнес-процесс не обрушится.
Какой опыт мы получили
Если резюмировать, мы сделали такие выводы:
-
Tarantool — это не реляционная СУБД, а швейцарский нож. Будь готов к сюрпризам!
-
От контура к контуру могут быть проблемы и лишения.
-
Без изучения и понимания всего Tarantool — никуда.
Понятно, что Tarantool — это не реляционная база данных. Это швейцарский нож — вставляешь его в проект, и он может сделать что угодно. Главное — правильно с ним обращаться. Мы не знали, точно ли справимся с таким инструментом, поэтому рассматривали Postgres и подобные БД, когда начали двигаться между контурами. В энтерпрайзе всегда есть несколько контуров, в том числе контур разработки, контуры для интеграционного, функционального, нагрузочного тестирования, предпромышленный контур, контур для промышленной эксплуатации.
Когда сталкивались с багами в Tarantool, думали, а не переехать ли на Redis. Но потом осознали всю прелесть Tarantool: благодаря ему мы можем без особой переработки данных поместить их туда, и Tarantool будет быстро отрабатывать их по нескольким индексам, поддерживая realtime с минимальным latency.
-
Flink с Tarantool прекрасно сосуществуют.
Первичная гипотеза, что Tarantool дружит с Flink, оказалась корректной. На Хабре в разных исследованиях много информации, как Flink дружит с Redis и другими кэшами, но про дружбу с Tarantool информации не нашел. А у нас они прекрасно сдружились.
-
Нужно покрывать тестовыми кейсами максимально всё.
Готовьтесь, что тестировать нужно всё. Потребуются не только функциональные тесты того, что вы разработали — баги могут быть в исходных версиях ПО.
-
Быстрая переработка архитектуры информационной системы.
Ваша архитектура будет постоянно меняться. Самое главное — заложить изначальный концепт так, чтобы он легко перестраивался, не ломаясь. То есть кубики и квадратики в архитектуре менялись, но это не влияло глобально на ваш процесс и информационную систему.
Рекомендации тем, кто пойдёт по нашему пути
Вот несколько советов для тех, кто решит последовать нашему примеру:
-
прорабатывайте архитектуру с учётом, что она поменяется;
-
выделите сервисы, логику которых не будете дополнять;
-
изучите ваши технологии — будьте готовы к изменениям;
-
ожидайте, что часть команды покинет вас;
-
подбирайте техстек под команду, а не иначе;
-
ждите багов и тестируйте!
Как говорил один военачальник, на поле битвы первым делом гибнет план этой битвы. Поэтому в условиях жёстких сроков и постоянно изменяющихся требований нужно использовать всеми нами любимый Agile. Часто Agile «натягивается на глобус» везде, нужно это или нет. Так вот: тут ему самое место.
В управлении командами должна быть гибкость на всех этапах проекта, начиная с архитектуры, заканчивая точечными техническими решениями. Вы должны быть готовы сменить базу данных, резидентное хранилище и перегруппироваться уже в процессе. То есть, если вы выкидываете любой кубик и вставляете новый, ничего особо не меняется.
Если решите следовать Scrum, нужно будет следовать его традициям и базовым церемониями. Это действительно классно помогает.
Часть команды может вас покинуть. Важно понимать, что даже если вас покинула часть команды, вам всё равно необходимо продолжать делать продукт. Заложите себе бэкап, чтобы кто-нибудь ещё мог это сделать.
Наш проект продолжает развиваться в полноценный продукт по цифровому маркетингу. Реализована первая часть, в процессе вторая с большими объёмами данных. Об этом мы обязательно расскажем на Highload 2025.
Автор: vladimir_lov