Тюнинг производительности
Оптимизация Meridian под высокую нагрузку — параллелизм, размеры очередей и memory-пулы.
Обзор
Meridian предоставляет три ключевых параметра, влияющих на пропускную способность, потребление памяти и поведение при обработке запросов:
| Параметр | Область | По умолчанию | Описание |
|---|---|---|---|
| Concurrency | На провайдера | 1000 | Количество worker-горутин, обслуживающих запросы одновременно |
| Buffer Size | На провайдера | 5000 | Максимум запросов в очереди до блокировки/отказа |
| Initial Pool Size | Глобально | 5000 | Предварительно выделенные объекты в sync-пулах для снижения нагрузки на GC |
Значения по умолчанию подходят для большинства production-развёртываний с нагрузкой до ~5000 RPS. Для бо́льших нагрузок или ограниченных окружений тонкая настройка этих параметров заметно улучшает производительность.
Как работают параметры
Concurrency (на провайдера)
Что делает: управляет двумя аспектами производительности провайдера:
- Worker-горутины — число горутин, обслуживающих запросы для каждого провайдера. Каждый воркер забирает запросы из очереди провайдера и выполняет их против его API.
- Прогрев пула провайдера — заранее выделяет provider-specific объекты ответов (например,
AnthropicMessageResponse,OpenAIResponse) в sync-пулах, чтобы снизить аллокации в рантайме.
Эффект:
- Бо́льшая concurrency — больше параллельных запросов к провайдеру, выше throughput, больше предвыделенных объектов;
- Меньшая concurrency — меньше параллельных запросов, ниже потребление ресурсов, меньше риск упереться в rate limit провайдера.
По умолчанию: 1000 воркеров на провайдера.
{
"providers": {
"openai": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 100,
"buffer_size": 500
}
}
}
}Buffer Size (на провайдера)
Что делает: задаёт ёмкость буферизированного канала (очереди) у каждого провайдера. Входящие запросы складываются туда, прежде чем попасть к воркерам.
Эффект:
- Больший буфер — больше запросов укладывается во время всплесков нагрузки; лучше переживает burst-трафик;
- Меньший буфер — меньше памяти, более быстрая backpressure-сигнализация клиентам.
По умолчанию: 5000 запросов на очередь провайдера.
Поведение при переполнении управляется флагом drop_excess_requests:
false(по умолчанию) — новые запросы блокируются до освобождения места;true— новые запросы немедленно отклоняются с ошибкой при переполнении.
Ограничение. Buffer size должен быть не меньше concurrency. Если concurrency > buffer_size, инициализация провайдера завершится ошибкой.
Initial Pool Size (глобально)
Что делает: задаёт количество объектов, предварительно выделяемых во внутренних sync-пулах Meridian при старте. Пулы переиспользуют объекты и снижают нагрузку на сборщик мусора.
Что находится в пулах:
- сообщения каналов (request wrappers);
- response-каналы;
- error-каналы;
- stream-каналы;
- plugin pipelines;
- request-объекты.
Эффект:
- Большее значение — меньше GC-давления на пиках, более стабильная latency, больше памяти на старте;
- Меньшее значение — меньше начальных аллокаций; под нагрузкой возможны дополнительные runtime-аллокации.
По умолчанию: 5000 объектов на пул.
{
"config": {
"initial_pool_size": 10000,
"drop_excess_requests": false
}
}Рекомендации по подбору значений
Concurrency и Buffer Size (на провайдера)
Настраивайте на каждого провайдера в зависимости от ожидаемой нагрузки:
| Нагрузка на провайдера | Concurrency | Buffer Size |
|---|---|---|
| 100 RPS | 100 | 150 |
| 500 RPS | 500 | 750 |
| 1000 RPS | 1000 | 1500 |
| 2500 RPS | 2500 | 3750 |
| 5000 RPS | 5000 | 7500 |
| 10000 RPS | 10000 | 15000 |
Пример. Если ожидается 2000 RPS к OpenAI и 500 RPS к Anthropic, настройте OpenAI с concurrency: 2000, buffer_size: 3000, а Anthropic — с concurrency: 500, buffer_size: 750.
Формула:
concurrency = ожидаемое_RPS
buffer_size = 1.5 × ожидаемое_RPSЭто соотношение даёт:
- запас очереди для всплесков трафика;
- работу воркеров без простоев;
- срабатывание backpressure до исчерпания памяти.
Initial Pool Size (глобально)
Подбирается на основе суммарной нагрузки по всем провайдерам:
| Суммарный RPS | Initial Pool Size | Оценка памяти |
|---|---|---|
| 100 | 150 | ~50 МБ |
| 500 | 750 | ~100 МБ |
| 1000 | 1500 | ~200 МБ |
| 2500 | 3750 | ~400 МБ |
| 5000 | 7500 | ~800 МБ |
| 10000 | 15000 | ~1,5 ГБ |
Оценки памяти приблизительны и зависят от размеров запросов и ответов, числа провайдеров и плагинов. Контролируйте фактическое потребление в своём окружении.
Формула:
initial_pool_size = 1.5 × суммарное_RPSДополнительно убедитесь, что:
initial_pool_size >= max(buffer_size по всем провайдерам)Так пулы будут прогреты до пиковой глубины очередей и не потребуют runtime-аллокаций.
Multi-node-развёртывания
Если несколько инстансов Meridian работают за балансировщиком, поделите параметры на число узлов, исходя из суммарного RPS.
Формула
Per-Node Concurrency = Total Concurrency / Number of Nodes
Per-Node Buffer Size = Total Buffer Size / Number of Nodes
Per-Node Initial Pool = Total Initial Pool / Number of NodesПример: 10 000 RPS на 4 узлах
Суммарная ёмкость (по всем 4 узлам):
- Total RPS: 10 000;
- Per-node RPS: ~2500 на узел.
Параметры одного узла, если его одного хватит на 10 000 RPS:
- Concurrency: 10000;
- Buffer Size: 15000;
- Initial Pool Size: 15000.
Параметры на узел при 4 узлах и общем 10 000 RPS:
| Параметр | Суммарно | На узел (4 узла) |
|---|---|---|
| Concurrency | 10000 | 2500 |
| Buffer Size | 15000 | 3750 |
| Initial Pool Size | 15000 | 3750 |
{
"config": {
"initial_pool_size": 3750,
"drop_excess_requests": false
},
"providers": {
"openai": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 2500,
"buffer_size": 3750
}
},
"anthropic": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 2500,
"buffer_size": 3750
}
}
}
}Kubernetes HPA. При использовании horizontal pod autoscaler настройки следует задавать под минимальное число реплик. По мере scale up каждый узел берёт меньшую долю трафика. Удобно использовать переменные окружения или ConfigMap, чтобы динамически подстраивать параметры под текущее число реплик.
Подбор под конкретного провайдера
У провайдеров разные rate limit'ы и характеристики латентности. Настраивайте параметры независимо.
Лимиты провайдеров
| Провайдер | Типичные лимиты | Рекомендованная concurrency | Замечания |
|---|---|---|---|
| OpenAI | 500–10 000 RPM (зависит от tier) | 100–500 | Высокие тарифы поддерживают бо́льшую concurrency |
| Anthropic | 1000–4000 RPM (зависит от tier) | 50–200 | Более консервативные лимиты |
| Bedrock | На каждую модель | 100–300 | См. квоты AWS под ваш аккаунт |
| Azure OpenAI | На deployment | 100–500 | Настраивается на уровне deployment |
| Vertex AI | На каждую модель | 100–300 | См. квоты GCP |
| Groq | Очень высокая пропускная способность | 500–1000 | Спроектирован под высокую concurrency |
| Ollama | Локальные ресурсы | 10–50 | Ограничен локальным GPU/CPU |
Пример: смешанная конфигурация
{
"providers": {
"openai": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 200,
"buffer_size": 1000
}
},
"anthropic": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 100,
"buffer_size": 500
}
},
"groq": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 500,
"buffer_size": 2500
}
},
"ollama": {
"keys": [...],
"concurrency_and_buffer_size": {
"concurrency": 20,
"buffer_size": 100
}
}
}
}Поведение при переполнении очереди
Когда очередь провайдера достигает ёмкости, поведение Meridian определяется флагом drop_excess_requests.
Режим блокировки (по умолчанию)
{
"config": {
"drop_excess_requests": false
}
}- Новые запросы ждут освобождения места.
- Запросы не теряются.
- Может вырасти latency на пиках.
- Подходит для критичных нагрузок, где важен каждый запрос.
Режим отказа
{
"config": {
"drop_excess_requests": true
}
}- Новые запросы немедленно отклоняются при переполнении.
- Возвращается ошибка:
"request dropped: queue is full". - Сохраняется стабильная latency для принятых запросов.
- Подходит для real-time сценариев, где устаревшие запросы бесполезны.
Best practice. Для production-нагрузок используйте drop_excess_requests: true с буфером 1.5× concurrency. Это предотвращает исчерпание памяти и при этом нормально переживает разумные всплески трафика.
Мониторинг и диагностика
Ключевые метрики
| Метрика | Здоровый диапазон | Действие при превышении |
|---|---|---|
| Глубина очереди | < 50 % от buffer_size | Увеличить буфер или concurrency |
| Latency p99 | < 2× от среднего | Проверить rate limit'ы провайдера |
| Dropped requests | 0 | Увеличить buffer_size |
| Потребление памяти | Стабильно | Уменьшить размер пулов и буфера |
| Число горутин | Стабильно | Проверить goroutine-leak'и |
Health-эндпоинт
Шлюз отдаёт health- и metrics-эндпоинты:
# Health-проверка
curl http://localhost:8080/health
# Prometheus-метрики
curl http://localhost:8080/metricsКраткая сводка лучших практик
- Начинайте консервативно. Стартуйте с меньших значений и масштабируйте по мере необходимости. Перерасход ресурсов — пустая трата.
- Постоянно мониторьте. Глубина очередей, latency, ошибки — настраивайте параметры по реальным паттернам трафика.
- Соотносите с лимитами провайдеров. Concurrency не должна превышать rate limit провайдера: больше — значит просто упираться в rate limit.
- Закладывайте на всплески. Buffer size = 1.5× concurrency покрывает всплески без потерь запросов.
Шпаргалка
// Формула
concurrency = ожидаемое RPS
buffer_size = 1.5 × ожидаемое RPS
initial_pool_size = 1.5 × суммарное RPS (по всем провайдерам)
// Пример: 500 RPS на провайдера, 2 провайдера (1000 RPS суммарно)
concurrency: 500, buffer_size: 750, initial_pool_size: 1500
// Пример: 2000 RPS на провайдера, 3 провайдера (6000 RPS суммарно)
concurrency: 2000, buffer_size: 3000, initial_pool_size: 9000
// Multi-node формула
per_node_value = total_value / number_of_nodesСвязанные документы
- Настройка провайдеров — полное руководство по настройке провайдеров.
- Маршрутизация запросов к провайдерам — как Meridian распределяет нагрузку между провайдерами.
Маршрутизация запросов к провайдерам
Как Meridian маршрутизирует запросы между LLM-провайдерами через governance-правила и адаптивную балансировку.
Custom-провайдеры
Несколько экземпляров одного и того же базового провайдера, ограничения по типам запросов, описательные имена и контролируемые шаблоны доступа.