Перейти к основному содержанию

Краткий ответ

Длительность в журнале консоли и тайм-аут вашего клиента измеряют не один и тот же промежуток.
  • Длительность в журнале идёт до того момента, когда шлюз завершает обработку;
  • Ваш клиент не считается завершившимся, пока не придёт последний байт body ответа и соединение не сигнализирует о завершении.
Так что ситуация «журнал показывает завершение за 280 секунд, но мой тайм-аут в 600 секунд так и не получил никаких данных» вполне возможна — и этот запрос действительно был успешным, и тарификация за него действительно была выполнена. Разрыв находится в сегментах, которые журнал вообще не охватывал.На этой странице показано, как измерить этот разрыв, привязать его к конкретному сегменту и определить правильную причину.
Эта страница посвящена вызовам без потоковой передачи с большими ответами: эндпоинты изображений, возвращающие base64, — классический случай (body ответа имеют размер от нескольких MB до десятков MB), и долгий текстовый вывод без потоковой передачи ведёт себя так же. Вызовы с потоковой передачей и небольшие ответы обычно не затрагиваются.

Что на самом деле учитывает длительность в логе

Общая задержка вызова делится на пять этапов:
Эта разница не отображается ни в одном поле лога.Мы провели внутреннее контролируемое сравнение с помощью raw-socket: для одного и того же пакета запросов наш бэкенд зафиксировал длительность в 5 секунд со статусом успеха, тогда как клиент фактически ожидал полного ответа 37–40 секунд. Эти 31–35 секунд пришлись на период после того, как шлюз завершил обработку, и ни одно поле длительности их не фиксирует.Это означает следующее: ссылаться на длительность из лога в ответ на «у меня запрос выполнялся целую вечность» ничего не доказывает — эти два значения никогда не противоречили друг другу. Для поиска проблемы необходим замер времени по отдельным этапам на стороне клиента.
Поля, доступные в консоли и в Log Query API: duration_for_view (длительность вызова в секундах), is_stream и request_id (указывайте его при обращении по поводу проблем).

Длительность учитывает только последнюю успешную попытку

Когда маршрут к апстриму перегружен очередью или возвращает ошибку, шлюз автоматически выполняет повторную попытку через другой маршрут в фоновом режиме. Длительность в логе учитывает только последнюю успешную попытку; предыдущие неудачные попытки не включаются в нее и не тарифицируются. Поэтому вы можете наблюдать следующую картину: первые 14 цифр request_id — это момент, когда шлюз получил запрос (UTC, YYYYMMDDhhmmss; добавьте 8 часов для UTC+8), тогда как время завершения в логе наступает на несколько минут позже, а длительность составляет всего несколько десятков секунд. Дополнительное время ушло на предыдущие неудачные попытки, а ваш клиент ожидает суммарное время тех и других.

Запросы без потоковой передачи не отправляют заголовки ответа до готовности результата

Вызов без потоковой передачи (обычный сценарий для генерации изображений) возвращает всё целиком после того, как результат полностью готов. Нативный эндпоинт Gemini ожидает, пока провайдер завершит создание изображения; совместимые эндпоинты, такие как /v1/images/edits, также конвертируют формат и сохраняют изображение в виде URL. До этого момента по соединению не передается никаких данных, даже заголовков ответа. Это нормально, однако означает, что соединение может находиться в полном бездействии от десятков секунд до нескольких минут. Если трафик проходит через прокси, VPN или собственный релей, они могут расценить это молчание как неактивное соединение и разорвать его: мы уже вернули и протарифицировали результат, но он так и не доходит до вашей программы. Информацию о том, как это проверить, см. в разделе Прокси сбрасывает запросы изображений примерно через 90 секунд.

Шаг 1: определите разрыв для сегмента с помощью одного curl

Это отправная точка для всего ниже. Сначала выполните это, затем решите, какой раздел подходит.
Три производные метрики превращают эти сырые числа в осмысленные сегменты:

Чтение результата

Сопоставьте ваши числа с этой таблицей — она определяет, что делать дальше:
Одного запуска недостаточно. Этот класс проблем приходит во временных окнах — внутри окна каждый вызов подряд затрагивается, а вне его десятки вызовов подряд работают совершенно нормально. Выполните это 10 раз, посмотрите на распределение и отметьте время суток вместе с часовым поясом.

Шаг 2: как измерять «данные пришли, но соединение так и не завершилось»

Если таблица указывает на третью строку, вам нужно более точное наблюдение: читайте ответ чанк за чанком, фиксируя время прихода каждого чанка и интервалы между ними. Вопрос, на который нужно ответить, — после прихода последнего байта как долго соединение ещё оставалось открытым?
Порог обнаружения: tail_99 свыше 30 секунд или max_gap свыше 30 секунд считается одним случаем «зависания хвоста». Признак — max_gap, возникающий в момент, когда счётчик байтов уже достиг 100%: каждый байт уже пришёл, и только после этого началось ожидание.
Не охватывайте три фазы одним значением timeout.Это самая простая ловушка: использование одного timeout для «ожидания первого байта» и «ожидания передачи» сводит две проблемы с совершенно разными корневыми причинами к одной неразличимой ошибке.Разделите их, и ваши журналы прямо покажут, было ли это «медленной генерацией» или «доставлено, но так и не завершено» — без догадок.

Шаг 3: измерение пропускной способности между двумя серверами

Между двумя машинами, которыми вы владеете

Используйте iperf3 для измерения реальной пропускной способности — это самый точный вариант:

От вашего сервера к нашему API

iperf3 нельзя использовать на этом участке — у нас не запущен iperf-сервер. Вместо этого измеряйте фактическую скорость по реальным вызовам:
Сочетайте это с проверками качества канала:
Если таблица результатов указывает на «сигнал завершения так и не пришёл», mtr и ping здесь бесполезны. В этом случае не потерян ни один байт, и качество канала в порядке, так что эти инструменты не покажут ничего плохого — и вы пойдёте по ложному следу. Перед тем как исследовать сеть, подтвердите с помощью скрипта выше, к какому классу вы относитесь.

Проверьте, достаточно ли вашей пропускной способности

Тело ответа с изображением — это один цельный блок base64. Измеренные объёмы: Само кодирование base64 увеличивает полезную нагрузку примерно на 33%. Время загрузки при канале к самому себе: Подвох в том, что эта таблица предполагает, что у вас есть канал к самому себе. На практике:
Например: исходящая скорость 10 Mbps, 30 параллельных запросов на изображения, 2.6 MB на ответ — каждому запросу достаётся примерно 0.04 MB/s, так что только загрузка занимает 62 секунды, и ни одна из этих секунд не появляется в журнале консоли. Удвойте параллельные запросы — и это число тоже удвоится.
Вот почему говорят: «днём во время нагрузки всё уходит в тайм-аут, а тот же код ночью работает нормально». Модель не замедлилась; вашу пропускную способность делят между собой больше запросов.

Шаг 4: какие поля должна записывать ваша инструментация

Чтобы точно описать симптом — для собственного анализа или чтобы отправить его нам — записывайте как минимум такие данные для каждого вызова: Этот последний столбец люди часто пропускают, но именно он нередко и есть ответ: постройте график скорости в зависимости от параллельных запросов, и если скорость падает пропорционально росту параллельных запросов, узкое место — это пропускная способность, и искать больше нечего. Как использовать таблицу: сопоставьте её с duration_for_view из лога консоли —
  • Если значения близки → проблема в downstream-передаче; проверьте пропускную способность и параллельные запросы;
  • Если значения далеко друг от друга → проблема в сигнале завершения или на стороне клиента.

Четыре меры, которые сразу снижают риск

1

Переключитесь на вывод URL — изменение с наибольшим эффектом

gpt-image-2-vip и gpt-image-2-all принимают response_format: "url", возвращая ссылку на изображение вместо base64. Тело ответа уменьшается примерно с 2.6 MB до примерно 0.3 KB — это одновременно устраняет проблемы со скачиванием и сигналом завершения, поскольку маленький ответ содержит Content-Length, и клиент сам понимает, когда всё завершено.Если ваш бизнес зависит от вывода URL, переключите группу token’а на image2_OSS: детерминированный вывод URL, который не будет деградировать до base64 при нехватке ресурсов, с коэффициентом тарифа 1x без наценки.
Официальный релей gpt-image-2 не поддерживает этот параметр и возвращает 400 unknown_parameter, если вы его отправите. base64 сейчас — его единственный путь вывода.
2

Уменьшите тело ответа

Когда вам нужно остаться на base64: output_format=jpeg с output_compression уменьшает размер более чем наполовину по сравнению с PNG; снижайте size и quality до того уровня, который вам действительно нужен, вместо того чтобы по умолчанию брать 4K. Сжатие входных референсных изображений до менее чем 1.5MB помогает и на этапе загрузки.
3

Ограничьте параллельные запросы уровнем, который поддерживает ваша пропускная способность

Переверните формулу выше: допустимое время скачивания × исходящая пропускная способность ÷ размер одного изображения — это ваш верхний предел параллельных запросов. Выше него большее количество параллельных запросов лишь замедляет каждый запрос, не повышая общую пропускную способность. Лимиты для каждой модели указаны в Сколько параллельных запросов я могу использовать?.
4

Разделите таймаут на три части и завершайте проактивно, как только данные получены

Установите отдельно таймаут первого байта, межчанковый таймаут и таймаут завершения с запасом, как описано выше. Когда данные уже получены, но сигнал завершения так и не приходит, передайте в ваше приложение ответ, который у вас уже есть — полный код совместимости приведён в Зависание завершения запроса изображения.

Часто задаваемые вопросы

Потому что тарификация происходит когда шлюз завершает обработку, и к тому моменту upstream действительно уже сгенерировал и вернул результат. Получит ли его ваш клиент после этого, на уже понесённую стоимость не влияет. Проверенное сравнение: клиент, который отключается через 5 секунд, тарифицируется абсолютно так же, как и тот, который работает до завершения.Если посмотреть с другой стороны, это самый надёжный диагностический признак: запись тарификации означает, что запрос действительно дошёл до upstream и успешно выполнился, значит, проблема должна быть либо после завершения обработки шлюзом, либо ещё до того, как запрос был по-настоящему отправлен — подозревать upstream не нужно.У image-эндпоинтов нет async task ID, поэтому при разрыве соединения результат теряется; см. Есть ли асинхронный image API?.
Это зависит — именно поэтому перед любыми изменениями нужно сначала измерить:
  • Медленная загрузка (низкая скорость, число байт всё ещё растёт): да, увеличение таймаута поможет вам получить результат.
  • Сигнал завершения так и не пришёл (байты полностью переданы уже давно, в конце нет ничего нового): нет. Мы измеряли ещё 330 секунд ожидания в этом состоянии без единого нового байта; более длинный таймаут лишь откладывает момент, когда вы это заметите. Здесь вам нужно завершать его на клиенте принудительно.
Это может быть и так и так, поэтому сначала нужно измерить. Вот критерии для обеих сторон:
  • Указывает на вас: явно низкий speed_download, скорость, которая падает по мере роста параллельных запросов, потери пакетов в mtr, либо curl работает нормально, а таймаут случается только в коде вашего приложения.
  • Указывает на нас: байты были полностью переданы уже давно, а в конце долгое время не приходит ни одного нового байта. У шлюза действительно была проблема с «отложенным сигналом завершения» — её первопричиной было то, что учёт тарификации на image-пути блокировал обработку запросов — исправлено и подтверждено релизом upstream 13 августа 2026 года. Даже в полностью здоровые периоды примерно 4% запросов всё равно ждут сигнал завершения ещё 10–79 секунд, при этом их данные уже полностью доставлены.
Когда у вас будут замеры по каждому сегменту, пришлите их нам; это гораздо полезнее, чем «это медленно». Поля, которые нужно включить, приведены в следующем разделе.
Генерация изображений сейчас везде синхронная, без эндпоинта для поиска по task ID. Вариант с асинхронной обработкой есть в дорожной карте и будет анонсирован отдельно, когда появится.До тех пор мы рекомендуем оборачивать асинхронную оболочку на своей стороне (возвращать локальный task ID при отправке, а фоновым worker’ам делать синхронный вызов); см. Построение своей async-очереди.
Зависит от того, в какой вы категории. Недостаточная пропускная способность — это свойство вашего egress, поэтому смена нашего адреса входа ничего не даёт — вам нужны большая пропускная способность, меньшие payload’ы или меньшая concurrency. Класс с сигналом завершения проявлялся одновременно на нескольких точках входа внутри одного окна и одновременно же восстанавливался, так что смена доменов тоже не помогает его обойти.Единственный адрес, которого следует избегать, — CDN-узел: api-cf.apiyi.com проходит через Cloudflare и возвращает 524 примерно через 100 секунд, что непригодно для долгих image-запросов.

Три легко упускаемых клиентских причины

Если curl показывает, что всё в порядке, а таймаут возникает только в коде вашего приложения, ищите здесь:
  1. Таймаут означает не то, что вы думаете. Ваши 600 секунд — это общий таймаут или только таймаут чтения? У Node undici есть три независимых таймаута — headersTimeout, bodyTimeout и connect.timeout — значения по умолчанию для которых намного ниже того, что вы задали на внешнем уровне, и изменение только внешнего таймаута не даёт эффекта.
  2. Очередь в connection pool. Когда pool переполнен, отсчёт начинается до фактической отправки request. Это ожидание для нас полностью невидимо — в backend log нет записи о request, пока он действительно не уйдёт. Диагностика: если соответствующей записи в log нет, обычно это именно этот класс причин.
  3. Между ними есть ещё один слой. У самостоятельно размещённого nginx значение proxy_read_timeout по умолчанию — 60 секунд, а load balancers, API gateways и serverless platform задают собственные верхние пределы. Проверьте таймаут на каждом переходе; самый маленький из них — ваш реальный таймаут.

Что включать в отчёт

Если после самопроверки вам всё ещё нужна наша помощь, отправьте всё это вместе, чтобы сэкономить несколько итераций:
  • Идентификаторы запросов (нескольких достаточно, не нужен полный набор)
  • Время по сегментам: время до первого байта / время до последнего байта / общее время / размер body ответа в байтах
  • Когда это произошло, с указанием часового пояса (например, 2026-08-13 15:57 (UTC+8))
  • Параллельные запросы на тот момент и ваша исходящая пропускная способность
  • Какую модель и какую группу token вы использовали

Связанные документы

Как избежать тайм-аутов API?

Рекомендуемые тайм-ауты для каждого сценария и выбор endpoint

Задержка завершения запроса на генерацию изображений

Обработка на стороне клиента, когда данные уже получены, но соединение не завершается

Обрывы соединения Image API

Диагностика downstream-обрывов связи в стиле ECONNRESET и SSL EOF

Какую параллельные запросы я могу использовать?

Лимиты параллельных запросов для каждой модели и запросы на квоту

Лучшие практики для Image API

Шпаргалка по тайм-аутам для каждой модели и сравнение форматов вывода

Чтение суммы тарификации в ваших логах

Что означает каждый столбец в логах консоли и как записывается тарификация

Свяжитесь с нами

Поддержка WeCom

QR-код поддержки WeComСканируйте QR-код или свяжитесь со службой поддержкиДиагностика тайм-аутов изображений и медленной загрузки

Электронная почта

Поддержка: [email protected]Бизнес: [email protected]