URL Shortener
Рефакторинг NodeJS → Java. Разделение путей чтения и записи. Редирект < 20ms, аналитика без ожидания.
Редирект < 20ms25+ измерений аналитикиЗадержка аналитики < 2 сек
Бизнес-задача
Сервис сокращения ссылок с детальной аналитикой переходов. Исходная система на NodeJS + MongoDB не справлялась: медленная аналитика, негибкие лимиты, сложности с масштабированием.
Ключевая задача
Редирект должен быть мгновенным, но при этом нужно записать ~20 параметров аналитики. Синхронная запись в БД убивает время отклика.
Решение: разделение путей чтения и записи. Редирект из in-memory кэша за < 20ms. Событие аналитики уходит в RabbitMQ без ожидания ответа — пользователь не ждёт записи.
Архитектура
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Nginx │────▶│ link-server │────▶│ Memcached │
└─────────────┘ └──────┬──────┘ └─────────────┘
│ async
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ RabbitMQ │ │ Redis │ │ backend │
│ (hits) │ │(sessions)│ │ (API) │
└────┬─────┘ └──────────┘ └──────────┘
│
┌────┴─────┐
│ClickHouse│
└──────────┘Специализация кэшей
Зачем два кэша: разные типы данных — разные требования.
- •Memcached — горячие ссылки. Без состояния, многопоточное чтение. При массовом чтении избегаем узкого места однопоточности
- •Redis — сессии, JWT-токены, счётчики ограничения частоты. С состоянием, персистентность между рестартами
ClickHouse: потоковая аналитика
MongoDB не справлялась с аналитическими запросами по миллионам записей.
RabbitMQ Table Engine для потоковой вставки без промежуточных преобразований. Данные доступны для отчётов с задержкой < 2 сек. ReplacingMergeTree для дедупликации повторных событий.
25+ измерений на каждый переход. Партиционирование по месяцам. Мгновенные срезы по любой комбинации.
Защита от ботов
Анализ трафика на лету: GeoIP + разбор UserAgent. Блокировка бот-сетей до попадания в аналитику. Стоп-лист с комплексной логикой: IP-диапазоны (CIDR), регулярные выражения по URL, комбинации AND/OR.
Почему микросервисы
Резолвер ссылок (редиректы) и приём аналитики имеют разные требования к масштабированию: ограничение по вводу-выводу vs ограничение по процессору. Независимое масштабирование каждого слоя.
Технологии
Backend
Java 11Spring BootSpring Data JPASpring SecuritygRPCLiquibase
Data
PostgreSQLClickHouseRedisMemcachedRabbitMQ
Infra
DockerNginxGitLab CI/CDMaxMind GeoIP2
Моя роль
Полный рефакторинг: от legacy NodeJS/MongoDB к микросервисной Java-системе. Архитектура, выбор стека, реализация ключевых сервисов (резолвер ссылок, API Gateway) и вспомогательных воркеров.
- Спроектировал асинхронный конвейер RabbitMQ → ClickHouse
- Реализовал гибридную схему кэширования (Memcached для чтения + Redis для данных с состоянием)
- Внедрил gRPC для межсервисного взаимодействия
- Настроил OAuth2 (Google, Яндекс) + JWT авторизацию