Продвигайте http abort/close из nginx в uwsgi/Django

У меня есть приложение для приложения Django, и мне было интересно, можно ли nginx распространить прервать/закрыть uwsgi/Django.

В основном я знаю, что nginx знает о преждевременном прерывании/закрытии, потому что по умолчанию он uwsgi_ignore_client_abort отключен, и вы получаете ошибки nginx 499 в ваших журналах nginx, когда запросы прерываются/закрываются до отправки ответа. Как только uwsgi завершает обработку запроса, он выдает "IO Error", когда он возвращается, чтобы вернуть ответ nginx.

Включение uwsgi_ignore_client_abort в "on" просто заставляет nginx не знать о прерывании/закрытии и удаляет uwsgi "Ошибки IO", потому что uwsgi все равно может записать обратно в nginx.

Моим вариантом использования является то, что у меня есть приложение, в котором люди просматривают некоторые результаты ajax очень быстро, и поэтому, если быстро перейти на страницу, я отменяю ожидающий запрос ajax на пропущенную страницу, это позволяет клиенту быть чистым и эффективным. Но это ничего не делает для серверной части (uwsgi/Django), потому что они все равно должны обрабатывать каждый отдельный запрос, даже если ничего не будет ждать ответа.

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

Так возможно ли это? uwsgi's Настройка hariakari заставляет меня думать, что это на каком-то уровне.... просто не могу понять, как это сделать.

Ответы

Ответ 1

Моим вариантом использования является то, что у меня есть приложение, в котором люди просматривают некоторые результаты ajax очень быстро, и поэтому, если быстро перейти на страницу, я отменяю ожидающий запрос ajax на пропущенную страницу, это позволяет клиенту быть чистым и эффективным.

Отмена запроса AJAX на стороне клиента осуществляется через XMLHttpRequest.abort(). Если запрос еще не отправлен при вызове abort(), запрос не будет выходить. Но если запрос был отправлен, сервер не будет знать, что запрос был прерван. Соединение не будет закрыто, на сервер не будет отправлено сообщение, ничего. Если вы хотите, чтобы сервер знал, что запрос больше не нужен, вам в основном нужно найти способ идентифицировать запросы, чтобы при первоначальном запросе вы получили для него идентификатор. Затем через другой запрос AJAX вы можете сообщить серверу, что предыдущий запрос должен быть отменен. (Если вы ищете вопросы о abort() как этот и найдите "сервер", вы найдете объяснения, говорящие то же самое.)

Обратите внимание, что uwsgi_ignore_client_abort - это то, что связано с закрытием соединений на уровне TCP. Это другое дело - отменить запрос AJAX. Как правило, в JavaScript нет действий, которые повлекут за собой закрытие TCP-соединения. Браузер оптимизирует создание и уничтожение соединений в соответствии с его потребностями. Только сейчас я сделал это:

  • Я использовал lsof, чтобы проверить, имел ли какой-либо процесс соединение с example.com. Их не было. (lsof - это утилита * nix, которая позволяет отображать открытые файлы. Сетевые подключения - это "файлы" в * nix.)

  • Я открыл страницу на example.com в Chrome. lsof показывает соединение и процесс, который его открыл.

  • Затем я закрыл страницу.

  • Я опросил с помощью lsof, чтобы узнать, было ли обнаружено ранее обнаруженное соединение. Он оставался открытым примерно через минуту после того, как я закрыл страницу, хотя не было никакой реальной необходимости поддерживать открытое соединение.

И нет никаких проблем с настройками uswgi, которые заставят его знать о прерываниях, выполняемых с помощью XMLHttpRequest.abort()

Сценарий прецедента, который вы дали, был тем, где пользователи быстро просматривали некоторые результаты. Я вижу две возможности для описания, заданного в вопросе:

  • Пользователь ждет обновления до дальнейшего поискового вызова. Например, Алиса просматривает список имен пользователей, отсортированных по алфавиту для пользователя "Zeno", и каждый раз, когда отображается новая страница, она видит, что имя отсутствует и страницы опущены. В этом случае ничего не нужно прервать, потому что действие пользователя зависит от запроса, который был обработан первым. (Пользователь должен увидеть новую страницу, прежде чем принимать решение.)

  • Пользователь просто пролистывает страницы, не дожидаясь обновления. Алиса снова ищет "Зено", но она считает, что она будет на последней странице, поэтому нажмите, щелкните, щелкните по ней. В этом случае вы можете отменить запросы, сделанные на сервере. Затем нажимается кнопка следующей страницы, увеличивайте количество страниц, которые должны отображаться пользователю, но не отправляйте право на запрос далеко. Вместо этого вы ждете небольшой задержки после того, как пользователь перестанет нажимать кнопку, чтобы отправить запрос с окончательным номером страницы, и поэтому вы делаете один запрос вместо дюжины. Здесь приведен пример debounce, выполненного для поиска DataTables.

Ответ 2

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

Это как раз та проблема, которая связана с тем, что она происходит так или иначе.

  • Очевидно, что вам не нужно продолжать тратить системные ресурсы на обработку, которая с тех пор была прервана, например, дорогостоящая операция поиска.

  • Но тогда, возможно, соединение было достаточно важным, что его еще нужно обработать, даже если клиент отключился.

    • Например, такая же дорогая операция поиска, но та, которая фактически не зависит от клиента, и будет кэшироваться nginx для всех последующих клиентов.

    • Или, может быть, операция, которая изменяет состояние вашего приложения - вы явно не хотите, чтобы ваше приложение имело несогласованное состояние!

Как уже упоминалось, проблема связана с uWSGI, а не с NGINX. Тем не менее, вы не можете uWSGI автоматически решить, каково было ваше намерение, без вашего раскрытия такого намерения самостоятельно uWSGI.

И как именно вы раскроете свое намерение в своем коде? Целая группа языков программирования не поддерживает многопоточные и/или асинхронные модели программирования, что делает ее полностью нетривиальной для отмены операций.

Таким образом, здесь нет магического решения. Даже у concurrency дружественных языков программирования, таких как Golang, возникают проблемы вокруг WithCancel context - вам, возможно, придется пройти его в каждом вызове функции, который может блокироваться, делая код очень уродливым.

Вы уже используете вышеуказанный контекст, проходящий в Django? Если нет, то решение является уродливым, но очень простым - в любое время, когда вы можете явно отказаться от запроса, проверьте, все ли подключен клиент к uwsgi.is_connected(uwsgi.connection_fd()):