Skip to main content

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

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

Что на самом деле покрывает длительность в логе

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

Шаг 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]