Как устроен современный банк? Есть бэк-офис, где выполняются разные операции, ведутся счета и отчетность. Есть мидл-офис, где принимаются решения и оцениваются риски, где оценивают кредитные риски и противодействуют мошенникам. И есть фронт-офис, где обслуживают клиентов и отвечают за их взаимодействие с банком через разные каналы.
В Сбербанке работают сотни систем разной доступности и надежности. Здесь есть и свои разработки, и коробочные решения с разной степенью кастомизации, разными SLA. Все системы интегрированы друг с другом огромным количеством способов. В этом посте мы расскажем, как весь этот муравейник фронтенда собирается таким образом чтобы обеспечивать непрерывное обслуживание клиентов.
Начнем с теории. Ключевые принципы, по которым строится отказоустойчивая система, можно позаимствовать у подводной лодки:
- Субмарина разделена на независимые отсеки. Если один отсек затопило, остальные все равно выживают.
- Все критичные компоненты зарезервированы. Двигатели, кислородные баллоны. А у Beatles зарезервированы еще и перископы с иллюминаторами.
- Подводная лодка защищена от критических условий на поверхности — при необходимости она может уйти вглубь и работать там как ни в чем не бывало.
Проиллюстрируем первый принцип примером из своей практики. Была у нас система с распределенным кэшем. И однажды под нагрузкой один из узлов данных кэша отказал. Ничего страшного: для соблюдения нужной репликации контроллер перераспределил данные на оставшиеся узлы. Но в результате перераспределения подскочил сетевой трафик и начали теряться пакеты — в том числе служебного трафика кэша. В один прекрасный момент контроллер решил, что отказала еще одна нода с данными, вновь перераспределил данные, трафик увеличился… В итоге, менее чем через минуту система легла целиком. К счастью, дело было на нагрузочном контуре и никто не пострадал. Но на поиск причины мы потратили немало времени.
Можно возразить, что с кластеризованными базами данных и high-end серверами такое не случается — там избыточность встроена на аппаратном уровне. Процитируем Вернера Вогельса, CTO Amazon: «Everything fails all the time». Падали у нас и кластеры БД, и high-end сервера. Падали из-за ошибок конфигурации, из-за проблем в управляющем ПО. С решением каждой проблемы наше доверие к такого рода решениям снижалось. В итоге мы пришли к выводу: не отказывают только те системы, которые разделены на независимые друг от друга части — в первую очередь, независимые по управлению.
Многоблочная архитектура
Решением проблем для нас стала многоблочная архитектура. В ней все аппаратные компоненты, включая базы данных, разделены на слабо связанные, практически независимые блоки. Каждый блок обслуживает часть клиентов, как при шардировании в базах данных. Узлы внутри каждого блока зарезервированы на всех уровнях, включая гео-резервирование. Любая проблема в одном блоке не влияет на другие. При увеличении числа клиентов мы можем легко добавить новые блоки и нормально работать дальше.
Общая архитектура блока. Все блоки зарезервированы по схеме 2N. В каждом ЦОД имеется производительный балансировщик аппаратной нагрузки. Дата-центры соединены 2-3 независимыми каналами связи.
Сервера распределены по блокам пяти типов:
- Маршрутизатор — управляющий блок, который распределяет клиентов по остальным блокам
- Клиентский блок — основной блок, обслуживающий до 10 млн клиентов
- Пилотный блок — здесь мы тестируем новые версии приложений на лояльных клиентах (примерно 300 тысяч человек, в основном сотрудники Сбербанка)
- Гостевой блок — через него обслуживаются не аутентифицированные пользователи; те, например, кто приходит через сайт
- Резервный блок — страховочный блок, достаточно мощный чтобы заменить сразу два клиентских блока
Внутри каждого блока сервера приложений и веб-сервера разделены по каналам обслуживания, но базы данных при этом общие. Так мы можем изолировать наиболее распространенные сценарии отказа, чтобы они не выходили за пределы своего канала.
Как это работает?
Сначала пользователь попадает в блок маршрутизатора. Этот блок проверяет, к какому клиентскому блоку относится человек, и отправляет его туда (или в гостевой блок). Дальше человек спокойно работает внутри своего блока. Если в родном блоке происходит отказ, человек возвращается к маршрутизатору и автоматически получает направление в резервный блок для дальнейшей работы.
Что происходит с данными во время работы? Информация о взаимодействии клиента с банком непрерывно реплицируется из клиентских блоков в архивную базу данных. Встретив пользователя, резервный блок подтягивает нужную информацию о нем из архивной базы и при необходимости выдает данные — так пользователь не подвисает при возникновении проблем с нашей стороны.
Операции, которые ведутся в резервном блоке, сохраняются в нем же. Когда родной клиентский блок пользователя восстанавливается, он переходит обратно. Операции, накопленные в резервном блоке, асинхронно переносятся в нужные клиентские блоки. Пока данные приводятся к консистентности, клиент видит сообщение о том, что все операции были приняты и сохранены, но из-за технических работ последние операции могут не отображаться.
Общая схема работы системы
В некоторых случаях переключение в резервный блок планируется заранее — например, при обновлениях в клиентском блоке. Тогда резервный блок не подхватывает сессии клиентского, а в определенный момент просто начинает все новые операции вместо него. Если необходимо экстренно переключиться на резервный блок, администратор может инвалидировать все сессии. При этом сессия пользователя прервется, и он начнет новую на резервном блоке. У блока маршрутизатора, кстати, есть свой выделенный резервный блок. Так что без запаски не остается никто.
Обновление систем
Новые версии ПО развертываем сначала на пилотном блоке и демонстрируются ограниченной аудитории. Затем постепенно на клиентских блоках, и уже в конце — на резервных. Так что если в клиентском блоке с новой версией ПО возникнут проблемы, мы можем перевести клиентов в резервный блок, со старой.
Когда на блок выкатывается новая функциональность, она не включается автоматически. Администраторы делают это с помощью флажков — feature toggle. Можно переключать клиентов на новую версию по группам — так мы проверяем реакцию обновлений на рост аудитории.
Автономность
Сама по себе наша система надежна, но все еще зависит от бэкенда, который используется для проведения операций. Как защититься от проблем? Мы используем три инструмента.
- Отложенные запросы. Клиент запрашивает выполнение операции. Мы сохраняем ее в своей БД и пытаемся исполнить в бэкенде. Если бэкенд не отвечает, мы показываем клиенту сообщение, что операция принята к исполнению и находится в обработке. Когда бэкенд поднимается, отдельный «докатчик» читает незавершенные операции из БД, и пачками «проталкивает» их в бэкенд-систему. Чтобы не перегрузить основную таблицу с операциями большим количеством низко эффективных запросов, дополнительно мы используем так называемую маркерную таблицу — список идентификаторов незавершенных операций. Чтобы не уронить только что поднявшийся бэкенд сотнями тысяч операций, используем батчинг — закидываем операций двести и ждем, например, несколько секунд.
А что если между запросом пользователя и восстановлением бэкенда произошли важные изменения? Например, сдвинулись курсы валют? В этом случае срабатывает двойная верификация. Данные операции сохраняются при вводе, а затем при исполнении сверяются. Если что-то не сходится, операция будут скорректирована или отклонена.
- Кэширование данных. Когда пользователь заходит, например, в Сбербанк Онлайн, вся нужная информация о нем там уже видна — счета, карты, кредиты и т.д. Эти данные запрашиваются через сервисную шину у десятка систем. Если ответ был собран быстро, за несколько секунд, мы показываем данные клиенту и сохраняем их в системном кэше своей БД. Если нет, то мы ищем в базе закэшированные ранее данные и показываем их клиенту. Конечно, для этого кэш должен быть не старше определенного возраста. Когда сервисная шина все-таки собирает нужные данные по запросу, они обновляются в кэше БД и отправляются клиенту взамен более старых.
При использовании приложения это означает, что человек увидит состояние своего счета максимум через несколько секунд после входа. Хотя данные и могут быть несколько устаревшими. Если так произошло, то через несколько секунд данные обычно заменяются актуальными — значит, сервисная шина собрала все что нужно.
Кроме того, у нас работает предварительное кэширование с помощью репликации. В основном, для разных справочных данных. Мы заранее загружаем эти данные в бэкенд, клиент спокойно делает запрос на операцию, и мы его отправляем. Даже если системы, отвечающие за ведение данных, не работают, пользователю не придется лишний раз ждать.
- Технические перерывы. Если бэкенд-система упала или проходит техническое обслуживание, мы помечаем ее флажком. И тогда проходящие через нее операции сразу встречает отказ. Так мы сохраняем сервера приложений от переполнения запросами, ожидающими ответа по таймауту. В этом режиме может использоваться кэширование операций и данных, которые мы описали ранее. Технические перерывы выставляются под каждый интеграционный сценарий, вручную администратором или автоматически, на основе количества запросов.
В любом случае мы добиваемся того, чтобы максимально сократить ожидание пользователя — если вдруг есть проблемы, он сразу получает сообщение о невозможности операции. Мы стараемся свести количество таких сообщений к минимуму, поэтому увеличиваем время жизни некоторых кэшированных данных — это позволяет продлить нормальное взаимодействие с сервисами банка.
В некоторых сценариях кэшированием увлекаться не стоит — например, при выдаче наличных. Здесь возможно мошенничество со стороны клиента. Подобные операции в банкоматах и отделениях у нас не кэшируются. В интернет-банке с этим проще — мы принимаем заявку, потом обрабатываем ее или отклоняем.
В итоге, соблюдая принципы, описанные в статье, можно получить системы с доступностью 99,99% и выше.
Наши планы
Сейчас в планах — минимизировать time-to-market нашей единой системы, обеспечить омниканальность с учетом технических и бизнес-особенностей каналов. А также мигрировать легаси-системы с сохранением их работоспособности в процессе переезда.
Благодарим Романа Шеховцова за активную помощь в подготовке поста
Автор: Sberbank