Ключевая идеяСделать
централизованный “crawl plane” для одной доски (общая инфраструктура запросов, прокси, анти-429), а клиентскую часть — как
потребителей данных (правила мониторинга/фильтры/алерты), чтобы:
- не дублировать одинаковые запросы для разных клиентов,
- держать один “оркестр” лимитов и бэкоффов,
- масштабировать воркеры горизонтально.
Компоненты (высокоуровнево)1) API / Control Plane- Auth/Org/Users
- Plans & Billing: тариф, лимиты, оплата.
- Projects/Monitors: клиент описывает “что мониторить” (поисковые запросы/категории/фильтры).
- Delivery config: куда слать (webhook/email/telegram/slack), частота, дедуп.
Технологии: REST/GraphQL + Postgres.
2) Scheduler (планировщик)Задача: превращать “мониторы” клиентов в
план запросов к доске.
- Планирование по приоритету и тарифу.
- Коалесинг/дедуп: одинаковые запросы у разных клиентов → один fetch.
- Учитывает “сейчас нельзя” (cooldown из-за 429 по маршруту/прокси/endpoint).
Выход: кладёт задания в очередь
FetchQueue.
3) Rate Limit & 429 Orchestrator (центральный лимитер)Сердце системы для одной доски.
- Token-bucket/Leaky-bucket на нескольких уровнях:
- Per-client (1 rps / 10 rps)
- Per-plan group (Standard/Premium пул)
- Per-target (глобально по доске и по конкретным endpoint’ам)
- Per-proxy/IP (чтобы не убивать конкретный прокси)
- На 429: повышает “стоимость” маршрута и ставит backoff/cooldown.
Реализация: Redis (atomic LUA scripts) / KeyDB. Один источник истины.
4) Fetch Workers (краулеры)Горизонтально масштабируемые воркеры:
- получают задания из FetchQueue,
- перед запросом запрашивают “разрешение” у лимитера,
- выбирают прокси (Proxy Manager),
- делают HTTP, парсят, нормализуют,
- пишут результат в Raw/Normalized Storage,
- на 429/403/капчу: репортят в Orchestrator и решают retry.
Очередь: Kafka/RabbitMQ/SQS. Важно:
- приоритеты,
- retry с задержкой,
- DLQ.
5) Proxy Manager- Пул прокси с метриками здоровья: latency, success rate, ban rate, 429 rate.
- “Sticky” стратегии (сессии/куки).
- Ротация по правилам (в т.ч. регионы).
- Блокировка/разморозка прокси на cooldown.
Хранилище состояния: Redis + Postgres, метрики в TSDB.
6) Parse & Normalize Pipeline- HTML/JSON → единая модель “Listing”.
- Версионирование парсеров.
- Извлечение ключей дедупа: (source_id/url/hash title+price+seller+time…).
Хранилища:
- Raw store: S3/MinIO (html snapshots) + метаданные.
- Normalized: Postgres (+ ClickHouse опционально).
7) Diff/Events (что изменилось)- Дедуп новых объявлений.
- Детект изменений (цена/статус/описание).
- Генерация событий:
LISTING_NEW
, LISTING_UPDATED
, LISTING_REMOVED
.
Пишем в
Events stream (Kafka topic) и в таблицу событий.
8) Client Matching & Notifications- Матчинг событий под правила клиентов.
- Fan-out уведомлений с ограничениями:
- rate-limit per client,
- retries, dead-letter.
- Каналы: webhooks, email, TG/Slack, API polling.
Как обеспечить “эффективно для большого числа клиентов”A) Коалесинг запросов (самое важное)Если 100 клиентов мониторят “категория X + фильтр Y”, вы делаете
1 fetch.
- Нормализовать “query signature”.
- Scheduler ведёт “map signature → next_run_at”.
- Тариф влияет на:
- частоту выполнения,
- приоритет,
- глубину (страницы).
B) Двухуровневое планирование- Планируется signature.
- Внутри — доставка по клиентам.
C) Многоуровневые лимитыДаже если клиенту “можно 10 rps”, реально — только если:
- глобальный лимит позволяет,
- прокси-пул здоров,
- нет cooldown.
Поведение при 429 (алгоритм)- Воркер получает 429 и собирает данные.
- Репортит в Orchestrator.
- Orchestrator ставит cooldown и backoff.
- Retry с respect Retry-After.
- При массовых 429 — circuit breaker и пересчёт планов.
Очереди и топики (пример)fetch.jobs.high
fetch.jobs.low
fetch.retry.delayed
parse.results
events.listings
notify.jobs
С DLQ на каждом уровне.
Данные и схемы (минимум)Postgres- tenants, users, plans, subscriptions
- monitors
- signatures
- signature_runs
- listings
- listing_events
- deliveries
Redis- rate limit buckets
- cooldown keys
- proxy health
- signature locks
S3/MinIOClickHouse (опционально)Масштабирование и изоляция- Fetch workers — горизонтально.
- Redis — кластер, LUA-атомики.
- Proxy Manager — отключение/баланс/липкие сессии.
- Изоляция крупных клиентов:
- отдельные очереди/пулы,
- отдельные прокси-пулы.
Тарифы: что реально означает 1 rps / 10 rpsКвота клиента — это потребление из общего crawl plane, а не гарантированный прямой RPS.
Premium:
- чаще run signatures,
- большая глубина,
- меньшая задержка доставки,
- приоритет.
Standard: реже и меньшая глубина.
Жёсткий per-client rps = отказ от коалесинга и рост стоимости.
Минимальный стек (прагматично)- API: Symfony/Laravel/Node + Postgres
- Очередь: RabbitMQ или Kafka
- Redis: rate limit + cooldown
- Workers: Go/Node/Python
- Storage: S3/MinIO
- Observability: Prometheus + Grafana + Loki/ELK
Набросок потоков- Monitor → save.
- Scheduler → signature + plan.
- Run → FetchQueue.
- Worker: acquire tokens → fetch → parse → events.
- Matcher → notify jobs.
- Notifier → delivery + log.