Движение по магистрали без аварий. Или как передавать 1,5 терабайта в сутки и ни одного не потерять?
Привет, Хабр.
Меня зовут Владимир Евсеев, я Senior Java developer, Teamlead в SSP SOFT.
Наша команда приступила к масштабному проекту: системе, обеспечивающей транспортный уровень документооборота банка. Сегодня я расскажу, как мы справились с первым этапом: выстроили магистраль, способную передавать около 150 000 файлов в сутки, или 1,5 терабайта информации. Поделюсь, что получилось и что еще предстоит довести до совершенства.
Как мы выстроили движение по этой магистрали?
Итак, наша задача звучала так: создать систему, обеспечивающую транспортный уровень документооборота процессинга банка. Звучит уже масштабно, учитывая, что наш клиент — один из крупнейших банков на рынке.
Пару уточняющих вопросов заказчику, и вот уже нарисовалась наша большая проблема: обеспечить передачу до 150 000 файлов в сутки. Каждые сутки. Всегда.
Что мы предложили в качестве MVP?
Архитектура: микросервисы, общение через Kafka, самые нагруженные сервисы task-manager, agent.
Сервисы:
Task-manager — оркестратор операций над файлами. Следит за выполнением всех операций, при необходимости делает повтор, принимает решения о запуске операции.
Detector — обходчик, принимает расписание и файл-сервер, на котором мониторит файлы по маске файла. Уведомляет task-manager о появлении нового файла.
Agent — занимается исполнением операции над файлом: копирование, переименование, перемещение, удаление.
Archive — архиватор, архивирует файл и копирует в MinIO.
Notification — отправка уведомлений о состоянии файла на почту.
UI — интерфейс системы, реестр правил, включение, отключение, история выполнения.
Redis — как кэш системы.
Kafka — транспорт системы.
MinIO — временное хранилище и архив.
Oracle — персистентное хранилище правил и их истории.
Для того что бы выдерживать передачу до 150 000 файлов, применили Event-driven architecture, все события обрабатываются асинхронно. Самый нагруженный сервис это task-manager. В сутки ему необходимо обрабатывать около 1 миллиона сообщений и кэшировать их в редис. Поэтому для реализации выбрали Project Reactor и реактивный драйвер для Redis от Lettuce (https://lettuce.io/). Для подключения к Kafka и маршрутизации сообщений используем Spring Integration.
Detector построен на Spring Integration, он из коробки позволяет создать flow, который может мониторить файл-сервер на наличие файлов по маске. Мы лишь немного докрутили логику создания flow. А именно: создаем flow по запросу от менеджера, добавили логику с регистрацией flow и поправили обработку исключений.
Если обходчик не может подключится к хосту сервера, то он будет пробовать снова и снова, делаем через события Spring. Spring Integration предоставляет маршрутизацию, нам это важно, потому что есть несколько протоколов (FTP, SMB, SFTP). Еще одним плюсом Spring Integration является то, что он реализует паттерн Control Bus. Это позволяет перезапускать бины в рантайме, а это важно, потому что каждый обходчик — это бин, который запускается по cron-расписанию.
Agent — для управлением ЖЦ операции. Используем Completable Feature — есть возможность остановить таску, это тоже часть нашего функционала. Ну и не забываем про асинхронный подход.
Archive и Notification — ничем не выделяются, поэтому на них останавливаться не будем.
Нагрузочное тестирование
Эту всю красоту мы не могли не протестить. Развернули standalone конфигурацию и дали нагрузку 300 000 файлов за сутки, т.е. за час система должна перекладывать ~13 000 файлов при среднем размере 10 Мб. Первый прогон показал, что task-manager падает с ошибкой CommandTimeoutException. Это говорит о том, что конфиг клиента сделан не правильно.
Исправили, докрутили пул коннектов, докинули 1 ЦПУ (изначально был 1 ЦПУ) и, о чудо, task-manager начал с бешеной скоростью обрабатывать и сохранять кеш в Redis! Результат 3 000 файлов в час, это примерно 72 000 файлов в сутки при standalone конфигурации. Развернуть кластер из 2-х нод, получаем 144 000 файлов в сутки.
Второй прогон: с менеджером все ок, но теперь падает agent потому что быстро забиваем thread pool, так как task-manager со скоростью звука ддосит агента. При этом система продолжает работать, но очень сильная деградация. Подкрутили настройки агента, ЦПУ, RAM. Результат 4 000 файлов в час, это примерно 96 000 файлов в сутки при standalone конфигурации. Развернуть кластер из 2х нод, получаем примерно 200 000 файлов в сутки.
Третий прогон: настроили кластер из двух нод, дали ту же самую нагрузку, получили 8 000 файлов в час, что доказывает передачу 192 000 файлов в сутки.
В итоге у нас получилось переложить 8 000 файлов в час, но не получилось гарантировать заказчику, что так будет всегда.
Что ж, опытные разработчики хорошо знают, что далеко не всё получается запустить с первого раза. Я считаю, что фундамент для нашей магистрали выстроен отличный.
Движемся дальше и работаем над гарантиями по транспортировке файлов. Подмигните в комментах, если интересно. Будем держать вас в курсе.