Перейти к содержанию
среда, 21 октября 2026 г.

ИИ Вестник

Главные новости о развитии искусственного интеллекта в России

Кэш для POST-запросов к LLM: как шлюз на Java 21 выдал 4 900 RPS на Redis

Источник: Все публикации в потоке Разработка
Server racks glowing in data center, engineer monitoring performance dashboards, photorealistic editorial scene.
Generated by Sourceful Riverflow (RouterAI)

Команда разработки описала собственный реактивный шлюз, который научил Redis кэшировать POST-запросы к OpenAI, DeepSeek и гео-сервисам, где стандартные прокси бессильны. Решение на Java 21, Spring WebFlux и Netty позволило выжать 4 900 RPS и убрать до 40% дублирующихся обращений к платным API.

Инженерная команда крупного enterprise-продукта столкнулась с нелинейным ростом счетов за токены LLM и лимитов внешних API: корпоративный чат-бот и аналитические модули начали потреблять шестизначные суммы в месяц. Анализ логов показал, что до 40% запросов от пользователей и внутренних сервисов представляют собой дубли — люди многократно переспрашивают у модели схожие аналитические промты, а фронтенд-модули массово отправляют идентичные запросы к геокодерам. Первым порывом было закрыть проблему классическим кэшем, но команда уперлась в архитектурный тупик, связанный с природой POST-трафика.

Стандартные инструменты вроде Nginx, Varnish или HAProxy создавались для веба, где ключевым является GET-запрос, а уникальным ключом кэширования выступает URL. Современные ИИ-модели и гео-сервисы работают строго через POST, а полезная нагрузка лежит в теле запроса в виде тяжелого JSON. Этот JSON постоянно мутирует: клиентские SDK добавляют динамический мусор вроде request_id, UUID сессий, nonce и временных меток, порядок ключей может меняться случайным образом, а лишние пробелы и переносы строк ломают побайтовое сравнение. Для обычного прокси два запроса с одинаковым промтом, но разными request_id — совершенно разные сущности, поэтому кэш улетает в мусорку, а писать кастомные Lua-скрипты под Nginx для глубокого парсинга и сортировки тяжелых JSON на лету команда сочла занятием для сильных духом.

В качестве альтернативы был разработан собственный легковесный реактивный On-Premise шлюз на Java 21 с использованием Project Loom и виртуальных потоков, Spring WebFlux и Netty. Цель — удерживать тысячи параллельных RPS на одном-двух ядрах процессора за счет неблокирующего ввода-вывода и не добавлять задержек. Сердцем системы стал CacheProxyHandler: он перехватывает входящий POST-запрос, очищает JSON от метаданных по гибкому конфигу провайдера, сортирует ключи по алфавиту, считает SHA-256 и проверяет хеш в Redis. Конфигурация маршрутов задается в application.yaml с отдельными base-url, TTL и списками exclude-fields для OpenAI и DeepSeek — например, для первой модели исключаются request_id, user_session_id и created_at, для второй — generation_id, client_timestamp и trace_id.

Первая реализация конвейера на стандартном ReactiveRedisTemplate<String, String> с нормализацией через Jackson показала неожиданные результаты при нагрузочном тестировании. На 3000+ RPS графики утилизации памяти поползли вверх, время отклика p(99) хаотично прыгало от 5 до 120 мс, а приложение регулярно ловило микро-паузы Stop-The-World. Профайлер выявил причину: StringRedisSerializer на каждый запрос выделял память в куче под новые массивы char[] и строки, перекодируя бинарные данные из Redis в Java-строки UTF-8, и при высокой плотности запросов сборщик мусора просто не успевал вычищать этот строковый мусор. Решением стал полный отказ от промежуточной конвертации JSON в строки при общении с базой: прокси-сервер перекладывает сырые байты из сетевых сокетов Netty напрямую в Redis и обратно.

Для этого конфигурацию Redis переписали, жестко зафиксировав бинарный raw-пасс для значений: ключ остался строковым, а значение стало сырым byte[] с эффектом zero-copy. В хэндлере логика чтения и записи перешла на работу с массивом байт — байты из Redis превращаются в строку ответа для клиента напрямую, а свежий ответ от API сохраняется в кэш уже в бинарном виде. Такой подход устранил лишние аллокации объектов и снизил давление на Garbage Collector, что критично для удержания стабильной latency при тысячах параллельных соединений. По сути, команда применила принципы, знакомые по высоконагруженным бинарным протоколам, к области, где традиционно доминируют текстовые форматы и тяжелые SDK. В результате шлюз достиг 4 900 RPS на одном-двух ядрах, а доля попаданий в кэш среди дублирующихся запросов приблизилась к 40%, что напрямую сократило расходы на токены и снизило нагрузку на внешние API.

Контекст проблемы типичен для российского enterprise-рынка: интеграция LLM-моделей и тяжелой гео-инфраструктуры вроде Google Maps, Яндекс.Карт и DaData есть практически в каждом крупном продукте, а счета за токены и лимиты API растут нелинейно. Для российских компаний, работающих с OpenAI и DeepSeek через посредников или собственные On-Premise развертывания, экономия на дублирующихся запросах означает не только снижение затрат, но и снятие рисков упереться в квоты внешних провайдеров. Собственный шлюз на Java 21 выглядит прагматичной альтернативой как облачным API-шлюзам с биллингом за каждый вызов, так и самописным Lua-решениям на Nginx, требующим глубокой экспертизы в парсинге JSON на лету. Кроме того, решение полностью соответствует курсу на импортозамещение и контроль данных: On-Premise-развертывание позволяет не отдавать чувствительные промты третьим лицам.

Открытыми остаются вопросы универсальности подхода: насколько гибко можно описывать правила нормализации для разных провайдеров без роста сложности конфигурации, как вести себя при потоковых ответах LLM, где кэширование частично неприменимо, и какие стратегии инвалидации нужны при изменении промтов или версий моделей. Тем не менее кейс показывает, что значительная часть затрат на ИИ-инфраструктуру устраняется на уровне грамотно спроектированного кэша, а не за счет урезания функциональности продукта. Дальнейшее развитие подобных шлюзов, вероятно, пойдет в сторону встроенной аналитики попаданий, адаптивных TTL и поддержки распределенных кластеров Redis с сохранением бинарного пути данных.

Читайте также