Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Асинхронность неизбежна: как мы проектировали с...

Lamoda Tech
December 20, 2024

Асинхронность неизбежна: как мы проектировали сервис уведомлений

Леша Ситка, старший Go-разработчик в Lamoda Tech

Распиливание монолита — одна из сложных задач, где разработчик постоянно думает о том, как спроектировать новую часть системы так, чтобы она работала отдельно, и оценивает спроектированное. Как в этом процессе избежать ошибок прошлого и заложить возможности в будущем? Об этом расскажу в докладе про реализацию нашего сервиса уведомлений. Разберем, как технические требования влияют на работу с контекстами, и что может происходить с архитектурой до написания первого кода

Lamoda Tech

December 20, 2024
Tweet

More Decks by Lamoda Tech

Other Decks in Programming

Transcript

  1. • Программирую с прошлого века • 13 лет в коммерческой

    разработке • Создавал и распиливал монолиты • Проектировал микросервисные приложения из десятков подсистем • Основной стек Golang, PHP, Node.JS Обо мне
  2. • функциональность сервиса уведомлений была размазана по основному монолиту BOB,

    обслуживающему флоу заказа • определили контекст уведомлений и приняли решение о его выделении • накопили требования для отдельного сервиса • отпиливание очередного куска упирается в нехватку отдельного сервиса уведомлений Предыстория разработки сервиса:
  3. быстро подключаться к любому значимому доменному событию иметь возможность гибкой

    настройки уведомлений изолировать синхронные запросы в соседние системы от основных процессов легко встраивать новые транспорты сообщений рассылать сообщения в определенное время часового пояса пользователя возможность экономии денег через каскад транспортов с эскалацией их стоимости Требования к новому сервису: 1 2 3 4 5 6
  4. Элементарный сервис уведомлений получили событие → создали уведомление → отправили

    сообщение Консьюмер Рендеринг Отправка Консьюмер топика
  5. Требование #1 — быстро разрабатывать и подключаться к любому значимому

    событию в системах компании заказ подтвержден заказ прибыл в пункт выдачи заказ выдан торговому представителю деньги за возврат заказа поступили на карту ваш код авторизации заказ отменен ссылка на изменение пароля наступает последний день хранения
  6. Модуль 1. Собираем потоки входящих событий Нужна модель реакции на

    события из внешних систем, позволяющая определять необходимость этих событий без доработок или с минимальными доработками в мастер-системах
  7. Модуль 2. Унификация потока внутренних событий С точки зрения сервиса

    уведомлений все входящие в него события обладают единым контекстом информирования: текст и условный адрес его доставки. Следовательно, результатом работы каждого консьюмера должно быть унифицированное событие уведомления с единой структурой.
  8. компонент, где разные события превращаются в унифицированное событие уведомления Читатель

    топика Рендеринг Отправка Читатель топика Консьюмеры топиков Модуль 2. Унификация потока внутренних событий Диспетчер внутренних событий Идентификатор Тип (триггер) Набор параметров
  9. Требование #2 — необходим функционал настройки уведомлений по разным параметрам:

    • стране • типу заказа • типу доставки • селлеру • методу доставки • etc
  10. Модуль 3. Блок маппинга — сопоставление потока событий с настройками

    уведомлений Рендеринг Отправка Консьюмер топика Диспетчер внутренних событий Маппинг
  11. Отправка Рендеринг Маппинг Модуль 4. Рендеринг — гибкая шаблонизация сообщений

    Рендеринг предусмотрен, но для подготовки текста сообщения могут понадобиться дополнительные данные, доступные по API. Консьюмер топика Диспетчер внутренних событий
  12. Модуль 5. External Data Provider (XDP) External Data Provider Отправка

    Рендеринг Маппинг результат запрос Консьюмер топика Диспетчер внутренних событий
  13. Схема сервиса на данный момент проектирования Постановка на отправку External

    Data Provider Рендеринг Маппинг результат запрос Консьюмер топика Диспетчер внутренних событий
  14. Модуль 7. Шедулинг - отправка по расписанию Шедулинг Постановка на

    отправку External Data Provider Рендеринг Маппинг Консьюмер топика Диспетчер внутренних событий результат запрос
  15. Буфер со скользящим указателем База недостаточно быстрая Поэтому используем структуру

    ZRangeByScore в Redis Ключ сортировки - временная метка timestamp Значение — маршализованное событие 1733900000 1733900000 1734000000 1734005000 []byte… []byte… []byte… []byte… now выбираемые при запросе 1734000000 1734100000 []byte… []byte… 1734100000
  16. Расширяем использование External Data Provider Шедулинг Постановка на отправку External

    Data Provider Рендеринг Маппинг Консьюмер топика Диспетчер внутренних событий
  17. External Data Provider - незаменимый компонент Шедулинг Постановка на отправку

    External Data Provider Рендеринг Маппинг Консьюмер топика Диспетчер внутренних событий
  18. Требование #6 — возможность экономии денег через каскад транспортов с

    эскалацией стоимости Как может выглядеть план отправки: • Одно СМС • Пуш + инбокс (сообщение в мобильном приложении) • Каскад из пуша + инбокса, затем СМС
  19. Модуль 8. Реализация плана отправки Шедулинг Постановка на отправку External

    Data Provider Рендеринг Маппинг Обработка статусов доставки Диспетчер флоу Таймаут ожидания статуса Консьюмер топика Диспетчер внутренних событий
  20. Постой, Алексей, а при чём тут Golang? • Способ организации

    инстансов облегчает компоновку процессов # консьюмеры кафки # основной конвейер обработки # демоны фоновых процессов # обработчики первичной статистики • Пул воркеров с семафором для скоростной вычитки буфера шэдулера
  21. Немного фактуры Количество инстансов по группам: • api админки -

    1 • демоны фоновой активности - 4 • группа консьюмеров кафки А - 20 • группа консьюмеров кафки В - 12 • группа детекторов событий - 10 • основной конвейер сообщений - 20 • группа первичной статистики - 8 Статистика: • в среднем влетает из разных топиков более 1.5М событий в сутки • в пике в минуту бывает по 3к • этот поток порождает около 600к внутренних событий в сутки • которые превращаются в 450к уведомлений пользователям ежедневно
  22. • Нужен паттерн transactional messaging на внутренние очереди • Для

    детекторов событий понадобился отдельный инстанс, т.к. им нужен XDP • Нужна система ретраев на отправку сообщений • Для отправки сообщений нужен TTL, который обозначает актуальность события • Сервис накапливает данные, чтобы не разбухала БД, нужна подсистема их очистки О чём мы забыли и что обнаружили, когда всё было готово
  23. • Простая задача может оказаться сложной, будьте к этому готовы

    • Максимально проработанные требования — это ключ к успеху на старте Выводы
  24. • Простая задача может оказаться сложной, будьте к этому готовы

    • Максимально проработанные требования — это ключ к успеху на старте • Иногда требованиям нужно дать настояться, часто их уточняют Выводы
  25. • Простая задача может оказаться сложной, будьте к этому готовы

    • Максимально проработанные требования — это ключ к успеху на старте • Иногда требованиям нужно дать настояться, часто их уточняют • Поиск и выделение контекстов — важный навык, который нужно тренировать Выводы
  26. • Простая задача может оказаться сложной, будьте к этому готовы

    • Максимально проработанные требования — это ключ к успеху на старте • Иногда требованиям нужно дать настояться, часто их уточняют • Поиск и выделение контекстов — важный навык, который нужно тренировать • Всегда мыслите потоками данных — это позволяет смотреть на контексты шире Выводы
  27. • Простая задача может оказаться сложной, будьте к этому готовы

    • Максимально проработанные требования — это ключ к успеху на старте • Иногда требованиям нужно дать настояться, часто их уточняют • Поиск и выделение контекстов — важный навык, который нужно тренировать • Всегда мыслите потоками данных — это позволяет смотреть на контексты шире • Не всё может быть очевидно сразу, не надо гнаться за решением всех задач Выводы