Архитектура воркеров
👁 436↗ 5
Коротко о проекте, чем сейчас я занимаюсь.
Мы разрабатываем криптоинвестиционный DeFi кошелек. В котором пользователь может видеть статистику по своему портфелю, совершать Dex обмены и самое интересное, входить в различные инвестиционные стратегии типа: [”curve.fi”, “frax.finance”, “balancer.fi”, “yearn.finance”, …].
И вот в этом проекте мы столкнулись с такой задачей, что требуется отдавать пользователям данные по транзакциям, балансам и одновременно с этим еще подтягивать эквиваленты цен в долларах.
Конечно же для МВП нет необходимости поднимать собственные ноды и парсить транзакции из различных сетей, чтобы агрегировать данные. Поэтому мы используем уже готовые ресурсы для того чтобы получить проиндексированные данные.
Но вот в чем боль. Такие ресурсы ограниченны количеством запросов, а платные тарифы дорого использовать для стартапа.
Поэтому делать простой прокси сервис, не кажется рациональной идеей.
Давайте посчитаем.
Предположим у нас есть 50 одновременно зашедших пользователей на сайт. В нашем кошельке используется 6 сетей: [”Ethereum”, “Binance Smart Chain”, “Polygon”, “Avalanche”, “Arbitrum”, “Optimism”].
Следовательно, чтобы получить все транзакции по этим сетям, мы уже делаем 6 запросов, чтобы получить данные по балансам, еще 6 запросов, итого 12 запросов, а мы еще даже исторические данные не подтянули, чтобы отображать изменение баланса за период.
Итого: 12 запросов умножаем на 50 пользователей = 600 запросов.
Обычно такие ресурсы выдают только 100к запросов в месяц, как думаете через какое время они закончатся? На нашем опыте, 2 дня !😟
Решение: применить популярную банковскую архитектуру. Обновлять данные по пользователям в фоновом режиме за счет очереди запросов и воркеров.
Вы наверное замечали, когда заходили в банковское приложение и там первично показывались старые балансы и спустя несколько секунд появлялись обновления.
Сейчас у нас все запросы отправляются в очередь (RabbitMQ) и на другой стороне есть сервисы, которые берут данные из очереди и начинают процесс обновления и сохранение данных в базу.
Чтобы каждый пользователь не ждал своей очереди, мы отдаем сразу быстро ему данные, которые уже хранятся в базе, а повторный запрос вернет новые данные.
Следующая проблем это “повторные запросы”. Для решения этой проблемы мы просто будем использовать сокеты, таким образом, пользователь будет видеть тот самый эффект обновления как в банковском приложении.
Также, чтобы избежать спама со стороны клиента, мы ввели небольшой TTL для запросов, чтобы в очередь могли отправляться пакеты от каждого пользователя с какой-то определенной периодичностью, например не чаще чем раз в 30 секунд.
Вишенкой на торте, стало решение для сокращения количества запросов, это использовать еще один сервис, который отдает общий баланс по пользователю сразу по всем сетям одним запросом и это позволяет сделать всего один запрос, чтобы понять требуется ли обновить данные по этому пользователю или нет. Если баланс изменился хоть на один сатоши, то запрос будет отправлен, если изменения нет, то и обновлять нечего.
Любая архитектура строится на компромиссе “скорость отдачи данных” против “актуальности этих данных”.
Нельзя получить одновременно и то и другое, эти параметры лежат на разных концах архитектурной шкалы. Поэтому такие решения как кэширование, смещают этот параметр в сторону “скорости отдачи данных” и ухудшают “актуальность”. Так и наша архитектура имеет тот же эффект.
Как говорил Боб Мартин, лучшая архитектура та, которую можно в будущем изменить. Поэтому мы должны стремиться к гибкости и возможности внесения изменений в будущем. Это позволит нам адаптироваться к новым требованиям и технологиям, и сохранить нашу систему актуальной и эффективной.
Как вам такой формат статей из личного опыта? Вообще мне очень нравится обсуждать архитектуры проектов, это один из самых творческих процессов в программировании.
Обсуждаем тут: @kyb_chat
Подписываемся тут: @knowyourbackend
1041