API-шлюз на реальных браузерах для платформы без API
Боевой REST API и флот браузерных воркеров в Kubernetes перед крупной потребительской веб-платформой, у которой нет публичного API.
- Область
- Интеграции и автоматизация
- Наша роль
- Реверс-инжиниринг, архитектура, бэкенд-разработка, DevOps и сопровождение
- Год
- 2025-2026
Мы сделали стабильный документированный REST API для бизнеса, чьи операции завязаны на крупную потребительскую веб-платформу без публичного API. Платформа подписывает запросы VM-обфусцированным JavaScript-шифрованием прямо на странице и закрыта WAF и антибот-защитой, поэтому шлюз выполняет каждый вызов в настоящей браузерной сессии Playwright. Флот браузерных воркеров в Kubernetes под управлением Temporal асинхронно ведёт онбординг аккаунтов, активацию кодов и заказы на пополнение, мониторинг встроен.
Задача
- Бизнес клиента зависел от сторонней платформы без партнёрского API: поиск профиля, проверка и активация кодов, заказы на пополнение — всё это доступно только через сайт.
- Сайт собирает и подписывает запросы VM-обфусцированным JavaScript-шифрованием: логику исполняет байт-код-интерпретатор, встроенный в страницу, и обычный HTTP-клиент не может сформировать валидный вызов.
- WAF и антибот-защита отсекают трафик, который не похож на цельную браузерную сессию, а правила риск-контроля со временем меняются.
- Для операций нужны авторизованные сессии: их надо создавать, проверять, обновлять и выводить из оборота, и каждая привязана к сетевому маршруту нужного региона.
- Партнёрам нужен стабильный контракт, идемпотентная отправка заказов, предсказуемое время ответа на чтение и понятные финальные статусы — при том что платформа меняется без предупреждения.
Решение
Исполнителем у нас работает настоящий браузер, а перед ним стоит аккуратный API. Мы разобрали шифрование платформы настолько, чтобы понять, как формируются запросы, и сознательно не стали его переписывать: шлюз вызывает собственный код подписи платформы внутри «тёплых» браузерных сессий. Так интеграция проходит те же шаги, что и браузер обычного пользователя, и не ломается, когда защиту пересобирают. Шлюз на FastAPI отвечает на быстрые запросы из пула браузерных контекстов на одном общем Chromium в поде. Долгие операции идут как workflow в Temporal на отдельном флоте воркеров, с повторами и бюджетом времени. В PostgreSQL лежат аккаунты, сессии и пул маршрутов от нескольких провайдеров, а сервис мониторинга шлёт алерты в командный чат.
Архитектура
Что умеет система
- Три API
- Внутренний, партнёрский и административный API, у каждого своя OpenAPI-документация и API-ключи с ролями. У партнёрского API урезанный и стабильный контракт ответа.
- Асинхронные заказы
- Заказ принимается с ответом 202 Accepted и необязательным ключом идемпотентности, результат клиент забирает опросом. Для внутренних вызовов есть синхронный вариант.
- Быстрое чтение на тёплых контекстах
- Поиск профиля и проверка кода — это один вызов на странице в уже готовом браузерном контексте, поэтому ответ приходит примерно за время одного запроса к платформе.
- Жизненный цикл аккаунтов и сессий
- Онбординг аккаунтов, прогрев и плановое обновление сессий работают как workflow в Temporal, рядом — синхронизация пула маршрутов и очистка логов использования.
- Две стратегии активации
- Высокопроизводительный конвейер на пуле сессий и последовательный режим, который держит работу за уже сложившимся аккаунтом.
- Мониторинг и chat-ops
- Алерты о росте очереди, доле ошибок и здоровье маршрутов приходят в командный чат. Оператор включает и выключает провайдеров маршрутов прямо из чата, без передеплоя.
Инженерные решения
- Пул браузеров переделан после утечки в проде: схема «один процесс на инстанс» оставляла деревья процессов, и мы перешли на один общий Chromium в поде с пулом контекстов, балансировкой по давности использования, проверкой здоровья каждые 15 секунд, мягкой ротацией и fail-fast-выходом, после которого Kubernetes чисто перезапускает под.
- Цельная браузерная идентичность: каждая сессия живёт под одной идентичностью, прогревается так, как сайтом пользуется живой посетитель, и переиспользует свои cookies. Именно поэтому интеграция стабильно работает за WAF и антибот-защитой.
- Никаких гонок за общие пулы: аккаунт и маршрут резервируются одной транзакцией с построчной блокировкой и пропуском занятых строк, а транзакции в базе никогда не охватывают внешний I/O.
- Каждый ответ платформы классифицируется: воркер либо продолжает на той же сессии, либо начинает заново со свежим браузером, аккаунтом и маршрутом. Повторы укладываются в бюджет времени заказа.
- Каждая задача — отдельный деплоймент в Kubernetes, масштабируемый числом реплик, с shared memory в памяти для Chromium и пробами; схемой данных владеет один сервис, у него 32 версионированные миграции Alembic.
Интерфейс
Схемы
Стек
- Python 3.12
- FastAPI
- Playwright
- Chromium
- Temporal
- PostgreSQL
- SQLAlchemy
- Alembic
- Docker
- Kubernetes
- Traefik
- S3
- structlog
- Locust
Что это показывает
- Надёжный документированный API поверх платформы, у которой API нет, включая реверс-инжиниринг VM-обфусцированного JavaScript-шифрования на странице.
- Бэкенд на браузерах, который выдерживает прод: пулы, жизненный цикл, восстановление после падений и порядок с процессами в контейнерах.
- Долгие и сбойные бизнес-процессы, оформленные как устойчивые workflow с понятными финальными статусами.
- Долгосрочное сопровождение боевой системы, пока платформа продолжает меняться; разборы инцидентов напрямую влияют на архитектуру.
Вопросы по кейсу
Зачем настоящие браузеры, если можно ходить в HTTP-эндпоинты платформы напрямую?
Платформа подписывает каждый запрос VM-обфусцированным JavaScript на странице и проверяет, что трафик похож на цельную браузерную сессию. Собственный код подписи платформы, запущенный в настоящей сессии Playwright, даёт корректные запросы без копирования чужой логики и продолжает работать, когда защиту пересобирают.
Что будет с заказом, если сессия на стороне платформы сломалась посередине?
Воркер классифицирует ответ. Если сессия больше не годится, он выводит её из оборота, освобождает маршрут, и Temporal повторяет заказ на свежем воркере в рамках бюджета времени. При окончательном отказе заказ сразу получает статус ошибки с понятной причиной и ссылкой на скриншот.
Как система масштабируется?
Каждый тип воркера — отдельный деплоймент в Kubernetes, который выполняет одну активность за раз на тёплом браузерном контексте. Пропускная способность растёт добавлением подов, а readiness-пробы шлюза уводят трафик с подов без готового контекста. Нагрузку и задержки проверяли в Locust.