Архитектурные решения, грабли и RabbitMQ

Привет!

Я Python-инженер. Последние несколько лет я в одиночку строил довольно сложную бэкенд‑систему, и за это время набил немало шишек и нашел, как мне кажется, несколько интересных решений. В этой статье я хочу поделиться не «историей успеха», а конкретными архитектурными проблемами и их решениями при построении высокопроизводительного сервиса на асинхронном Python.

Статья будет полезна тем, кто работает с FastAPI, микросервисами и думает о надежности и масштабируемости своих систем.

1. Проблема: Хаос из 20+ AI-моделей

Все началось с простой бизнес-задачи: предоставить унифицированный доступ к десяткам различных AI-моделей (от OpenAI и Gemini до Sora и Kling). Проблема в том, что каждое API - это свой мир:

  • Разные форматы аутентификации (API-ключи, JWT-токены).

  • Абсолютно разная структура запросов и ответов (image_url vs reference_image).

  • Разное поведение: одни отвечают сразу, другие требуют асинхронного опроса статуса.

Просто "подключать" их по очереди в каждом новом клиенте (например, Telegram-боте) - это прямой путь к неподдерживаемому спагетти-коду. Стало очевидно, что нужно централизованное решение - API-шлюз.

2. Архитектурное решение №1: Декаплинг с FastAPI и RabbitMQ

Для API-шлюза я выбрал FastAPI. Его асинхронная природа идеально подходила для I/O-bound задачи - принять запрос и быстро "перекинуть" его дальше.

Но главная проблема это долгие задачи. Некоторые AI-модели генерируют видео по 3-5 минут. Заставлять клиента ждать столько времени просто безумие.

Мой путь к очередям:

  1. Наивное решение: Сначала я реализовал очередь на MongoDB. API создавал в коллекции документ со статусом pending, а воркеры в бесконечном цикле опрашивали (poll) эту коллекцию. Это работало, но создавало лишнюю нагрузку на БД и было ненадежно.

  2. Правильное решение: Я понял, что изобретаю велосипед, и перешел на RabbitMQ. Это было ключевым решением. API-сервер (Producer) теперь просто публикует сообщение-задачу в очередь и мгновенно отвечает клиенту 202 Accepted. А отдельные воркеры (Consumers) разбирают эту очередь.

  3. Обеспечение надежности: Чтобы задачи не терялись, если воркер падает, я внедрил два стандартных механизма:

    • Acknowledgements (ack/nack): Воркер подтверждает получение сообщения только после успешной обработки. Если он "умирает", сообщение возвращается в очередь.

    • Dead Letter Queue (DLQ): "Сломанные" сообщения, которые вызывают ошибку несколько раз подряд, автоматически отправляются в отдельную очередь для ручного разбора, не блокируя основной поток.

3. Архитектурное решение №2: Правильная база для правильной задачи

Я использовал гибридный подход к хранению данных, и он себя полностью оправдал:

  • MySQL (PostgreSQL): Для всех структурированных, транзакционных данных, где важна целостность: профили пользователей, API-ключи, биллинг, тарифы.

  • MongoDB: Для оперативных, часто меняющихся данных: хранение статусов задач, их параметров и JSON-результатов. Гибкая схема и скорость чтения Mongo здесь подходят идеально.

4. Грабли: Как моя MongoDB раздулась до 8GB и чему меня это научило

А теперь о главной ошибке. На старте, для простоты, я передавал в очередь и сохранял в Mongo изображения в формате Base64. Это работало.

Проблема проявилась через несколько месяцев, когда я увидел, что коллекция с задачами раздулась до 8GB при всего 180,000 записей. Аналитика стала тормозить, бэкапы стали огромными. Я понял, что система скоро "встанет".

Решение: Я провел рефакторинг, внедрив паттерн "Pass by Reference". Теперь API-сервер не передает Base64 в очередь, а:

  1. Сначала загружает файл на S3-совместимое хранилище.

  2. Кладет в очередь и в Mongo только легковесную ссылку (URL) на этот файл.

Это простое изменение сократило размер базы на 90% и в разы ускорило все операции. Урок: никогда не храните тяжелые бинарные данные в оперативной базе данных.

5. Мой подход к разработке: "Дирижер и Оркестр"

Многие спросят, как я справился с этим в одиночку. Я активно использую AI‑ассистентов, но не как «автопилот», а как «оркестр». Моя роль это быть архитектором и дирижером:

  • Я определяю проблему («база раздувается»).

  • Я проектирую решение («нужно вынести файлы на S3»).

  • Я ставлю конкретную техническую задачу («напиши мне сервис для загрузки на S3»).

Этот подход позволяет мне фокусироваться на архитектуре и качестве, делегируя рутинную реализацию.

Заключение

Строить high-load систему в одиночку - это вызов. Ключевые уроки, которые я вынес:

  1. Не бойся использовать промышленные инструменты (как RabbitMQ) с самого начала. Они сэкономят тебе месяцы в будущем.

  2. Выбирай правильное хранилище для правильного типа данных.

  3. Проактивно ищи и исправляй «бутылочные горлышки», а не жди, пока все сломается.


P.S. Да, мне 18 лет, и я понимаю, что мой путь не совсем стандартный. Именно поэтому я и решил поделиться своим практическим опытом — надеюсь, он будет кому‑то полезен, и буду очень благодарен за конструктивный фидбек от более опытных коллег.

Мой GitHub

Комментарии (43)


  1. Keeper22
    18.10.2025 20:15

    200к+ запросов в сутки

    ≈ 2.3 запроса в секунду

    Высоконагруженные системы


    1. shai_hulud
      18.10.2025 20:15

      Разве для Python это не считается "Высоконагруженной системой"?


      1. mentin
        18.10.2025 20:15

        Даже для Ruby это не много


      1. IlyaEdrets
        18.10.2025 20:15

        Хайлоадом обычно считают систему, которая не справляется с нагрузкой на одном сервере.

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


      1. motoronik
        18.10.2025 20:15

        2.3 rps это мука для любого языка


    1. metheoryt
      18.10.2025 20:15

      Увидел заголовок и пришел негодовать именно по этой причине


  1. eto_on
    18.10.2025 20:15

    Кидали в базу картинки, исправили, назвали это highload? Ну ок


  1. Junecat
    18.10.2025 20:15

    "Высоконагруженные системы, которые мы заслужили"...


    1. unclejocker
      18.10.2025 20:15

      Со щепоткой вайбкодинга!


  1. BugM
    18.10.2025 20:15

    2 rps и 8 гигабайт данных это даже близко не высоконагруженная система.


  1. plFlok
    18.10.2025 20:15

    Аналитика стала тормозить

    1. Мне даже интересно, как лежащие и никого не беспокоящие блобы одной коллекции могли аффектить работу других коллекций

    2. использовать монгу для аналитики - мощно. Лучше сразу уходить в аналитические бд для этого. Всё равно с ростом проекта к этому придёте.

    3. и вообще хочется блобы в какой-нибудь s3 любого облака закинуть, даже если сам сервис крутится на vps-ке за 500 рублей. Ну реально, 8 гигов у яндекса на хранении стоят 16 рублей. первые 100к чтений бесплатно. следующие 100к - 4 рубля.
      Иногда лучше откупиться от задачи деньгами, чем тратить на неё время. Вспомните, сколько час программиста стоит, и сколько часов он будет тратить на поддержку решения в будущем. Точно облака не будут дешевле?

    p.s: 8 гб - это вообще можно всю базу в RAM держать. Очень удивлён, что оно вызвало тормоза.


  1. maxp
    18.10.2025 20:15

    Про "200к запросов в сутки" тут уже все высказались :)

    В конечном счете хранить бинари в S3 - это правильное решение.
    А вот разные дб лучше не тянуть в проект без веских оснований. Одного Постгреса вам тут хватило бы по уши. Но уж коль есть желание общаться с MongoDB, то на надо забывать, что там GridFS есть "из коробки".


    1. rdo
      18.10.2025 20:15

      Раньше это называлось пет-проектом, такое делали безо всяких ЛММ-ассастов и не писали про это такие пафосные статьи. Уровень крепкого джуна.


  1. tagabenz
    18.10.2025 20:15

    Молодец конечно, но пуху ты на себя накинул , 18 лет инженер, последние несколько лет)


  1. d3d11
    18.10.2025 20:15

    высокопроизводительного сервиса на асинхронном Python

    Разве Python может быть в одном предложении с высокой производительностью?


    1. Artyomcool
      18.10.2025 20:15

      Конечно может! У этого приема даже есть название: оксюморон.


  1. panzerfaust
    18.10.2025 20:15

    Что меня каждый раз удивляет: человек на хайпе пилит очередную свистоперделку про ИИ, но не догадывается спросить у этого самого ИИ, как это правильно делать.