Когда жалобы звучат так: «иногда клиент не получает ответ», почти всегда проблема не в одном “таймауте”, а в несогласованной цепочке ожиданий. В API такси это критично: задержка на стыке мобильной сети, Nginx и upstream может приводить к разным ошибкам, а часть клиентов успевает ретраить запросы или, наоборот, отменяет их раньше.

У Nginx таймауты размазаны по этапам жизненного цикла запроса. Один и тот же “запрос” может прерваться на чтении заголовков, на установке соединения к upstream, на чтении ответа, на отправке тела клиенту. Поэтому оптимизация начинается не с угадывания значений, а с понимания: какой именно этап истекает и что при этом видит клиент.

Ниже — карта этапов и то, какие директивы чаще всего оказываются причиной “потерь” в связке Nginx → API такси.

Где именно «теряются» запросы: типовые сценарии и статусы

Под “теряются запросы” обычно подразумевают одно из пяти: клиент отменил раньше, Nginx не дождался на своем этапе, upstream не ответил вовремя, ответ обрывается при отдаче клиенту, либо ретраи приводят к дублированию или кажущейся потере.

Клиент ушёл раньше: статус 499

Если пользователь отменяет запрос, закрывает вкладку, уходит с экрана или приложение перезапускает запрос из-за тайм-аута на своей стороне, Nginx фиксирует 499 (Client Closed Request). В этом случае запрос не “теряется” в Nginx — он прерывается клиентом. Тем не менее, для бизнеса это выглядит как потеря подтверждения заказа или статуса.

Что проверить:

  • реальный тайм-аут на клиенте (мобильное приложение, API gateway перед клиентом, SDK)
  • есть ли ретраи при сетевых ошибках и как они ограничены
  • идемпотентность критичных операций (создание заказа, подтверждение оплаты)

Nginx не дождался заголовков/тела клиента: 408

Если заголовки запроса приходят медленно (слабая сеть) и истекает клиентский таймаут, Nginx может вернуть 408 Request Timeout. Аналогично при слишком долгой отправке тела запроса (если клиент грузит JSON с вложениями или делает большие POST).

Это часто встречается в API такси при редких проблемах сети: приложение “подвисло” на передаче, а Nginx завершил ожидание.

Проблемы на установке соединения к upstream: 502 и 504

Когда Nginx не может установить соединение к upstream, срабатывает proxyconnecttimeout. Результат зависит от того, как настроены failover-правила, но типично это 502 (Bad Gateway) или 504 (Gateway Timeout), если попытка затянулась или upstream “полумёртв”.

Для API такси причины обычно такие:

  • upstream перегружен и TCP/handshake не успевает
  • DNS/адресация нестабильна (особенно если Nginx долго держит старые IP)
  • неудачная конфигурация keepalive между Nginx и upstream

Nginx не дождался ответа от upstream: 504

Самый “знакомый” сценарий. Если upstream подключился, получил запрос, но не успел отдать ответ до proxyreadtimeout, клиент получит 504 Gateway Timeout. Для такси это может быть сценарий “поиск водителя/расчёт стоимости/проверка доступности”: процесс в upstream длинный или периодически замирает.

Важно: proxyreadtimeout — не “максимальная длительность запроса”. Это таймаут ожидания между чтениями данных. Если upstream отправляет данные редко (например, долго думает, а потом отдает одним куском) — истечение всё равно произойдет.

Nginx не смог отдать ответ клиенту: обрывы и 499/504-похожие эффекты

Иногда upstream отработал, но Nginx не успевает передать ответ дальше из-за медленного клиента или ограничений. Тут вмешиваются send_timeout, а также поведение буферизации и режим передачи (буферизация/стриминг). В итоге клиент может отменить запрос (499), а вы видите “провалы” в доставке статуса.

Настройки Nginx, которые реально влияют на API

Таймауты делятся на клиентские и прокси-таймауты. Плюс отдельно стоят таймауты keepalive, которые сами по себе не “прерывают запрос”, но резко меняют частоту установления новых соединений и вероятность проблем на connect/read.

Ниже — директивы, которые чаще всего встречаются в конфигурациях Nginx для API такси.

clientheadertimeout и clientbodytimeout

Эти директивы относятся к тому, как долго Nginx ждёт заголовки и тело запроса от клиента. Если у вас мобильные клиенты на слабых сетях, слишком маленькие значения приведут к росту 408 и доле “оборванных” попыток ещё до проксирования в upstream.

Практическое правило: эти таймауты должны учитывать реальную сеть. Если ваши клиенты в большинстве случаев укладываются в несколько секунд, нет смысла ставить минуты. Но если бывают часы пик, где сеть “плывёт”, ужимать таймаут нельзя вслепую.

keepalive_timeout и связанные параметры

keepalive_timeout влияет на то, как долго Nginx держит простои для keep-alive соединений с клиентом. Когда таймаут слишком низкий, вы повышаете churn соединений: больше handshakes, больше нагрузка на сеть и больше шансов на connect-провалы при пиковых нагрузках.

Если же таймаут слишком высокий, вы держите много idle-соединений, что иногда упирается в лимиты по файлам/сокетам и ухудшает поведение под нагрузкой. Оптимизация тут всегда идет через наблюдаемость: когда растёт churn, увеличивается доля connect-ошибок; когда растут idle-залежи — упираетесь в ресурсы.

proxyconnecttimeout

proxyconnecttimeout — ожидание на этапе установки соединения до upstream. Если upstream становится недоступен, этот таймаут определяет, сколько Nginx будет “молчать” перед ошибкой.

Для API такси разумнее:

  • держать proxyconnecttimeout достаточно небольшим, чтобы не держать соединение висяком
  • но не настолько маленьким, чтобы банально не “убивать” медленные сети и перегруженный upstream в момент всплеска

proxysendtimeout

proxysendtimeout — время ожидания отправки данных от Nginx к upstream. Оно важно, когда запрос к upstream большой или upstream читает ответ медленно/рвано (или вы используете режимы без буферизации, где данные передаются потоково).

Если вы видите, что запросы “доходят” до upstream не всегда, попробуйте проверить сценарии:

  • клиент долго отправляет тело POST
  • Nginx буферизует/не буферизует и от этого меняется характер отправки
  • upstream “задерживает” чтение, и Nginx не успевает дозаписать данные

proxyreadtimeout

proxyreadtimeout — ключевой директивой в проблемах 504. Он отвечает за то, как долго Nginx ждёт чтение данных от upstream. Для API такси это часто “время обработки” + возможные задержки из-за зависаний downstream-сервисов.

Чтобы не гадать, ориентируйтесь на распределение задержек upstream:

  • если upstream обычно отвечает быстро, но иногда есть редкие долгие хвосты, proxyreadtimeout должен перекрывать типичный p99-диапазон обработки, а не среднее
  • если проблема в зависаниях, лучше чинить upstream, чем бесконечно увеличивать proxyreadtimeout

send_timeout

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

Нюанс: при стриминге и chunked-ответах простои между чанками становятся “видимыми” именно для этих таймаутов. Поэтому оценка должна идти вместе с тем, как именно upstream формирует ответ.

Минимальный пример конфигурации для API такси

Пример ниже не “идеал”, а каркас, который помогает мысленно разложить этапы. Таймауты выставляются под вашу наблюдаемую задержку, а не под универсальные значения.

«`nginx server { listen 443 ssl http2;

location /api/ { proxypass http://taxiapi_upstream;

proxyhttpversion 1.1; proxysetheader Connection «»;

proxyconnecttimeout 2s; proxysendtimeout 10s; proxyreadtimeout 15s;

send_timeout 10s;

clientheadertimeout 5s; clientbodytimeout 10s;

proxynextupstream error timeout http502 http503 http_504; proxynextupstream_tries 2; proxynextupstream_timeout 20s; } } «`

Два замечания:

  1. proxynextupstream добавляет ретраи внутри Nginx, и суммарная длительность запроса для клиента может стать больше, чем вы ожидаете.
  2. значения должны согласоваться с клиентскими ретраями. Иначе вы создадите ситуацию “клиент ретраит, Nginx тоже ретраит” — итогом станут дубликаты операций.

Диагностика: как найти, где отваливается запрос

Если вы меняете таймауты без диагноза, вы увеличиваете риск просто “перенести” точку отказа. Правильная диагностика обычно занимает меньше времени, чем кажется: нужна привязка ошибок к конкретному этапу (connect/read/send/client).

Добавьте подробные поля в access_log

Стандартного status иногда недостаточно. В логах важно различать:

  • upstreamconnecttime — насколько долго подключались
  • upstreamheadertime — сколько ждали заголовок ответа
  • upstreamresponsetime — сколько в сумме ждали ответ
  • request_time — сколько запрос длился “от клиента до готовности”

Пример формата:

«`nginx logformat apitaxi ‘$remoteaddr — $timelocal ‘ ‘»$request» $status $bodybytessent ‘ ‘rt=$request_time ‘ ‘uct=$upstreamconnecttime uht=$upstreamheadertime urt=$upstreamresponsetime ‘ ‘us=$upstream_status’;

accesslog /var/log/nginx/apitaxiaccess.log apitaxi; «`

После этого вы сможете уверенно ответить на вопрос: это проблема connect или read. Если rt высокий, но uct маленький — значит “долго читаем”. Если uct высокий — upstream “не поднимается”.

Сверьте Nginx-ошибки с клиентскими тайм-аутами

Если Nginx возвращает 504, но клиент видит тот же исход, ретраи на стороне клиента могут начинать “охоту” до того, как Nginx успевает отработать failover. Для API такси это опасно, потому что часть методов должна быть идемпотентной.

Практический подход:

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

Используйте error_log точечно

errorlog с уровнем notice или warn часто показывает причину: timeout on reading, upstream timed out, connect() failed и т.п. Если ошибок мало, достаточно обычного уровня логирования и правильного формата accesslog.

Временно включайте debug только при воспроизведении. Под нагрузкой debug раздувает логи и искажает картину.

Измерьте распределения задержек upstream

Таймауты в Nginx должны опираться на наблюдаемую статистику upstream, а не на “ощущения”. Минимальный набор:

  • p50/p95/p99 времени обработки запроса в upstream
  • доля запросов, которые уходят в хвост (tail latency)
  • причины медленных запросов внутри upstream (очереди, внешние вызовы, блокировки)

Как только вы видите “медленные причины”, таймауты перестают быть костылём.

Стратегия оптимизации таймаутов под бизнес-профиль API такси

Под API такси часто понимают набор операций с разными требованиями к SLA. Создание заказа может требовать быстрого ответа, а фоновая диспетчеризация — обрабатываться асинхронно. Если один и тот же proxyreadtimeout обслуживает всё подряд, вы либо получаете лишние 504, либо позволяете зависаниям “переползать” в очередь.

Ниже — практичная схема согласования таймаутов по этапам.

1) Разделите операции по чувствительности к задержке

Выделите группы:

  • короткие операции: проверка статуса, получение тарифов, чтение профиля
  • операции средней длительности: расчет и формирование маршрута
  • тяжелые операции: сложный поиск водителя, вызовы сторонних сервисов, обработка медиа

Для каждой группы задайте свой location или хотя бы отдельный набор настроек (через include-фрагменты или отдельные серверные блоки, если архитектура позволяет).

2) Выберите таймауты так, чтобы upstream успевал отдать ответ, а клиент не ждал бесконечно

Правильная логика иерархии выглядит так:

  • Nginx не должен ждать дольше, чем имеет смысл для вашего клиента и бизнес-потока
  • но и не должен обрывать upstream раньше, чем тот успевает в вашей целевой метрике p99

Практически это означает:

  • proxyconnecttimeout: под ваш худший сценарий соединения к upstream (учитывая сеть и возможные пики)
  • proxyreadtimeout: под верхнюю границу времени обработки в upstream для “нормальных хвостов”
  • clientheadertimeout/clientbodytimeout: под реальную мобильную сеть и паттерн загрузки

Если вы наоборот сделаете proxyreadtimeout очень большим, вы замедлите восстановление системы: соединения будут висеть дольше, что увеличивает нагрузку на пулы и очередь.

3) Учитывайте повторные попытки (retries) и идемпотентность

Любая настройка, которая увеличивает число таймаутов, увеличивает число повторов. В API такси это может привести к:

  • дубликатам заказов
  • повторному списанию или повторной фиксации статуса, если транзакции не защищены

Поэтому перед тем как “ужимать” таймауты:

  • убедитесь, что create-процессы поддерживают idempotency key (или аналогичный механизм)
  • ограничьте количество ретраев на клиенте и на Nginx так, чтобы суммарное окно не становилось бесконечным
  • логируйте идемпотентный ключ, чтобы быстро распознавать дубликаты

4) Не лечите зависания увеличением таймаута

Если upstream иногда зависает (а не просто “медленно работает”), высокий proxyreadtimeout лишь увеличит время жизни зависшего запроса. Это ухудшает пропускную способность и делает “хвосты” тяжелее для всей системы.

В таком случае вместо роста таймаутов:

  • ищите блокировки и очереди в upstream
  • проверяйте таймауты на внутренних вызовах (downstream HTTP/gRPC)
  • добавляйте circuit breaker/ограничители нагрузки

Таймауты Nginx должны быть верхней границей ожидания, а не способом “разрешить вечность”.

Типичные ошибки, из-за которых запросы проваливаются именно в Nginx

Ошибки часто повторяются, потому что люди берут дефолты или ставят “приблизительно большие числа”. В результате система ведёт себя неожиданно при пиках.

Ошибка 1: proxyreadtimeout выставлен слишком низко относительно хвостов upstream

Симптомы:

  • рост 504 в моменты нагрузки
  • upstreamresponsetime резко приближается к proxyreadtimeout

Решение:

  • измерить p99 и хвосты upstream
  • поднять proxyreadtimeout до уровня, где “нормальные” запросы успевают, но зависания ещё не консервируются надолго

Ошибка 2: proxysendtimeout упирается в отправку при стриминге

Если вы отключаете буферизацию или upstream отдает ответ чанками/потоком, простои становятся важными. На это смотрит proxyreadtimeout, но и при больших запросах к upstream критичным становится proxysendtimeout.

Решение:

  • определить, какие endpoints стримят
  • отдельно настроить timeouts для этих location
  • при необходимости включить буферизацию там, где она безопасна для вашего API-клиента

Ошибка 3: клиентский таймаут короче, чем Nginx (или наоборот) без учета ретраев

Если клиент отменяет запрос быстрее, вы получите 499 и “потери”, хотя Nginx дальше мог бы успешно ответить. Если Nginx обрывает раньше клиентского ретрая, вы создадите каскад дубликатов.

Решение:

  • согласовать таймауты клиента и Nginx
  • убедиться, что ретраи либо отключены для критичных операций, либо полностью идемпотентны

Ошибка 4: keepalive_timeout и поведение keep-alive приводят к всплеску connect-таймаутов

Когда keepalive не настроен как надо, соединения постоянно пересоздаются. Это особенно больно, когда upstream начинает отвечать медленнее: connect и TLS/handshake превращаются в узкое место, и proxyconnecttimeout начинает срабатывать.

Решение:

  • проверьте, что между Nginx и upstream используется ожидаемая версия HTTP и настройки Connection
  • проверьте, как вы настроили keepalive к upstream (если используете его)
  • сопоставьте всплески connect ошибок с ростом churn соединений

Ошибка 5: DNS и резолвинг для upstream приводят к “подключениям в никуда”

Nginx может кэшировать DNS-ответы. Если upstream в облаке меняет IP, Nginx какое-то время продолжит пытаться коннектиться к старым адресам и словит proxyconnecttimeout.

Решение:

  • убедиться, что настроен resolver и правильный valid для DNS
  • оценить, как быстро upstream меняет адреса в вашем окружении и соответствуют ли это ваши параметры

Ошибка 6: proxynextupstream расширяет суммарное время запроса

Nginx может попытаться отправить запрос к другому upstream при некоторых статусах/таймаутах. Но если не ограничить proxynextupstream_timeout и число tries, суммарное время может превысить ожидания клиента.

Решение:

  • ограничить общий бюджет времени на попытки
  • учитывать, что клиент тоже может ретраить
  • для идемпотентных операций разрешать retries активнее, для неидемпотентных — аккуратнее

Практический чек-лист: настройка и проверка

Ниже последовательность, которую удобно применять без “лотереи”. Она помогает найти реальную точку, где Nginx роняет запросы, и не сделать ситуацию хуже.

  • Снимите картину за период инцидента
  • доля 499/408/502/504
  • requesttime и upstreamresponse_time (по логам)
  • частоты по endpoints (какие пути чаще падают)
  • Определите, какой этап доминирует
  • если высокие uct: проблема в proxyconnecttimeout (или DNS/доступности)
  • если высокие urt: проблема в proxyreadtimeout (или зависания/очереди в upstream)
  • если запросы обрываются без 504: возможно, проблема в отправке/клиентских отменах (send_timeout или клиентский таймаут)
  • Разбейте настройки по категориям endpoints
  • короткие операции: строгие read/send таймауты
  • тяжелые операции: отдельный потолок proxyreadtimeout
  • стриминг: отдельные значения для send/read, с проверкой буферизации
  • Изменяйте один параметр за раз
  • сначала proxyconnecttimeout, если причина на connect
  • затем proxyreadtimeout, если причина на read
  • отдельно проверьте send_timeout, если присутствуют клиентские отмены и обрывы
  • Согласуйте с клиентом ретраи и идемпотентность
  • убедитесь, что повторные запросы не создают дублей
  • лимитируйте число ретраев и суммарный бюджет времени
  • Прогоните тесты на реальных профилях
  • тестируйте не только “успешный” сценарий, но и хвосты: замедленный upstream, частичные сбои downstream
  • смотрите, где возникает ошибка и какой статус возвращает Nginx
  • Закрепите метрики и алерты
  • алерт по росту 504 с привязкой к proxyreadtimeout-сценарию
  • алерт по росту connect ошибок (proxyconnecttimeout-сценарий)
  • алерт по 499, если клиент отменяет слишком часто (это тоже влияет на бизнес-метрики)

Пример: как понять, что проблема именно в proxyreadtimeout

Допустим, после нагрузки вы видите рост 504. Логи показывают закономерность:

  • requesttime и upstreamresponse_time почти совпадают и растут
  • upstreamconnecttime стабилен и небольшой
  • upstream_status часто похож по смыслу (или может быть пустым в случае тайм-аута чтения)

Это типичный портрет: upstream подключился, но не отдал ответ в срок. Тогда next шаг не “увеличить всё”, а подтвердить характер задержек:

  • есть ли в upstream очередь/блокировка
  • зависит ли обработка от внешних вызовов и их таймаутов
  • не сформирован ли ответ слишком поздно (например, собирается большими порциями)

После этого можно:

  • поднять proxyreadtimeout умеренно, если хвосты upstream “нормальные” и вы просто не успевали
  • или, если зависания системные, чинить upstream и внутренние таймауты вместо увеличения внешнего потолка

Заключение: как перестать терять запросы в Nginx для API такси

Оптимизация таймаутов в Nginx для API такси — это не подбор чисел, а точечная настройка по этапам: клиент читает заголовки и тело, Nginx подключается к upstream, отправляет запрос, ждёт ответ и отдаёт клиенту. “Теряются запросы” там, где истекает конкретный бюджет ожидания, а остальная часть цепочки продолжает работать или вообще не успевает.

Начните с диагностики: добавьте информативный access_log, разложите ошибки по connect/read/send/client и определите доминирующий этап. Затем согласуйте таймауты Nginx с клиентскими ожиданиями и ретраями, обязательно обеспечив идемпотентность для операций, которые нельзя повторять без последствий.

Если нужно, пришлите текущий фрагмент конфигурации Nginx для /api/ и примеры accesslog по нескольким “проваленным” запросам (статусы, requesttime, upstreamconnecttime, upstreamresponsetime). По этому набору можно довольно точно сказать, какой таймаут вы реально упираете и где именно запросы уходят в обрыв.