
В документации Nginx есть директивы, касающиеся трех различных тайм-аутов, которые можно настроить для «бэкэнд»-серверов следующим образом:
proxy_connect_timeout
Определяет тайм-аут для установления соединения с прокси-сервером. Следует отметить, что этот тайм-аут обычно не может превышать 75 секунд.
Это легко понять — Nginx собирается подключиться к вышестоящему «бэкенд-серверу», и если он не сможет подключиться в течение X времени, он откажется и вернет ошибку. Сервер недоступен, имеет слишком много подключений и т. д.
proxy_read_timeout
Определяет тайм-аут для чтения ответа от проксируемого сервера. Тайм-аут устанавливается только между двумя последовательными операциями чтения, а не для передачи всего ответа. Если проксируемый сервер ничего не передаст в течение этого времени, соединение закрывается.
Это также имеет смысл — Nginx уже установил TCP-соединение с «бэкэнд-сервером» и теперь собирается фактически отправить запрос, но серверу требуется много времени на обработку, и если это займет больше X времени, он закроет соединение и вернет управление пользователю.
Я на самом деле был удивлен, что Nginx закрывает соединение. Я думал, что он сохранит соединение, но вернет ошибку пользователю. Звучит дорого, чтобы заново устанавливать эти "бэкенд" TCP-соединения каждый раз, когда что-то истекает.
proxy_send_timeout
Устанавливает таймаут для передачи запроса на проксируемый сервер. Таймаут устанавливается только между двумя последовательными операциями записи, а не для передачи всего запроса. Если проксируемый сервер ничего не получает в течение этого времени, соединение закрывается.
Это я не совсем понимаю. У меня есть теория, но я хочу, чтобы кто-то ее подтвердил. Единственное значение, которое я могу придумать для этого тайм-аута, — если полезная нагрузка запроса огромна (например, большой запрос POST с JSON или документ, который пользователь хочет сохранить). Передача запроса на «бэкенд» потребует разбиения запроса на более мелкие сегменты MTU TCP и отправки их как «фрагментов» исходного запроса. Так что технически мы фактически не отправили запрос, пока не передали все фрагменты на сервер успешно. Измеряет ли Nginx время между каждым фрагментом запроса? Это то, что означает «запись» в документе? Как только запрос фактически отправлен, Nginx начнет измерять proxy_read_timeout
?
решение1
TCP/IP — это так называемый «потоковый» протокол передачи данных. Он разработан для того, чтобы позволить стороне, считывающей данные через соединение TCP/IP, не обязательно заботиться о размерах «сегментов» или даже пакетов. На практике это означает ситуацию, когда одноранговый узел вызывает некоторую традиционную операцию «чтения» для получения данных, отправленных удаленным одноранговым узлом (например,read
с Linux), не обязательно будет считывать за раз ровно столько данных, сколько удаленный конец предоставил для одной операции «записи». Реализация протокола TCP/IP неизменно будет создавать IP-пакеты соответствующего размера из того, что было передано в операцию «записи» за раз, а реализация на другом конце будет собирать данные обратно из этих пакетов; но она не будетобязательно передать их какому-то «читающему» клиентскому приложению с теми же границами данных!
Пример: У A есть 50 КБ данных для отправки для каждого внешнего системного события, так много, что они не помещаются в ОЗУ все одновременно, поэтому они отправляют их порциями по 16 КБ, что является размером их буфера отправки. Поэтому они сначала отправляют 16 КБ, затем еще 16 КБ, затем еще 16 КБ и, наконец, 2 КБ. Реализация TCP/IP может отправить эти 50 КБ, буферизованные в буфере 128 КБ внутри (например, буфер ядра), и только затем отправить их по сети, которая также имеет свои собственные условия. Некоторые из этих данных, фрагментированные таким образом, что отправляющее приложение даже не знает об этом, сначала поступают на другой конец — из-за сетевых условий — и собираются там реализацией TCP/IP и снова помещаются в буфер ядра. Ядро пробуждает процесс, который хочет прочитать данные — считывая все 30 КБ. Получатель должен решить, ожидает ли он большего и как понять, насколько больше ожидать —формат«сообщения» или данных не является предметом беспокойства TCP/IP.
Это означает, что Nginx не может знать, какой объем клиентских запросов он будет прочитан за один раз для каждого read
вызова, который он будет выполнять, например, в системе на базе Linux.
Хотя документация proxy_send_timeout
немного намекает на то, для чего это нужно (выделено мной):
Устанавливает таймаут для передачи запроса на проксируемый сервер. Таймаут устанавливается только между двумя последовательными операциями записи, а не для передачи всего запроса.Если прокси-сервер ничего не получит в течение этого времени, соединение закрыто.
Дело в том, что, поскольку Nginxпроксизапрос -- то есть запрос незарождатьсяс ним — он ждет, пока «нисходящий» клиент (удаленный конец соединения, отправивший запрос в Nginx, который последний в своей роли «прокси» теперь ожидает переслать вверх по течению) передаст данные запроса, прежде чем он перешлет (запишет) его по восходящему соединению.
Насколько я понимаю, если от нисходящего узла ничего не получено [в течение периода ожидания], то и проксируемый сервер тоже ничего не получит — и соединение будет закрыто.
Другими словами, если указанный нисходящий поток ничего не отправляет в течение периода времени, указанного proxy_send_timeout
, Nginx закроет соединение с восходящим потоком.
Например, рассмотрим веб-браузер, который отправляет запрос в Nginx. Первая часть считывается Nginx в момент времени A. Предполагая, что он будет проксировать запрос в какой-то апстрим, он открывает соединение с указанным апстримом и передает (записывает) то, что он получил от браузера, через сокет апстримного соединения. Затем он просто ждет, пока из браузера будут считаны другие части данных запроса — если следующая часть не прибудет по истечении некоторого тайм-аута X относительно времени A, он закроет соединение с апстримом.
Обратите внимание, что это не обязательно означает, что соединение с веб-браузером будет закрыто — он, безусловно, вернет какой-то код ошибки HTTP для запроса, но время жизни соединения с веб-браузером регулируется другим набором условий, а proxy_send_timeout
последний касается только подключений Nginx к восходящему потоку.