Что происходит, когда у вас есть бесконечный цикл в коде представления Django?

Что-то, о чем я только что подумал:

Скажем, я пишу код представления для своего сайта Django, но делаю ошибку и создаю бесконечный цикл.

Всякий раз, когда кто-то пытался получить доступ к представлению, рабочий процесс, назначенный запросу (будь то рабочий процесс Gevent или поток Python), оставался в цикле на неопределенный срок.

Если я правильно понимаю, сервер отправит клиенту ошибку тайм-аута через 30 секунд. Но что будет с рабочим Python? Будет ли он работать бесконечно? Звучит опасно!

Представьте, что у меня есть сервер, на котором я выделил 10 воркеров. Я позволяю ему работать, и в какой-то момент клиент пытается получить доступ к представлению с помощью бесконечного цикла. Ему будет назначен рабочий, и он будет фактически мертв до следующего перезапуска сервера. Опасность в том, что сначала я бы этого не заметил, потому что сайт просто стал бы незаметно медленнее, имея 9 воркеров вместо 10. Но потом это может повторяться снова и снова на протяжении длительного промежутка времени, может месяцев. Сайт просто становился все медленнее, пока, в конце концов, он не стал бы действительно медленным только с одним работником.

Перезапуск сервера решит проблему, но я бы не хотел, чтобы функциональность моего сайта зависела от перезапуска сервера.

Это реальная проблема, которая происходит? Есть ли способ избежать этого?

Обновление: я также был бы очень признателен за способ получить трассировку стека потока/рабочего, который застрял в бесконечном цикле, чтобы я мог получить его по электронной почте, чтобы я был в курсе проблемы. . (Я не знаю, как это сделать, потому что исключение не возникает.)

Обновление для людей, говорящих что-то вроде "Избегайте написания кода, содержащего бесконечные циклы": Если это не очевидно, я не трачу свое свободное время намеренно на включение бесконечных циклов в свой код. Когда такие вещи случаются, это ошибки, а ошибки можно свести к минимуму, но никогда полностью не избежать. Я хочу знать, что даже если я совершу ошибку, будет система безопасности, которая уведомит меня и позволит исправить проблему.


person Ram Rachum    schedule 27.04.2013    source источник
comment
интересное чтение: stackoverflow.com/questions/8685695/   -  person Glyn Jackson    schedule 27.04.2013
comment
Я обновил свой ответ, надеюсь, теперь он отвечает на ваш вопрос :)   -  person Krzysztof Bujniewicz    schedule 29.04.2013


Ответы (3)


Это настоящая проблема. В случае gevent, из-за переключения контекста, он может даже сразу же перестать отвечать вашему сайту.

Все зависит от вашего окружения. Например, при запуске django в продакшене через uwsgi можно установить harakiri — это время в секундах, по истечении которого нить, обрабатывающая запрос, будет убита, если не закончила обработку ответа. Настоятельно рекомендуется установить такое значение, чтобы иметь дело с ошибочными запросами или плохим кодом. О таком событии сообщается в журнале uwsgi. Я считаю, что другие решения для запуска Django в продакшене имеют аналогичные параметры.

В противном случае из-за сетевой архитектуры отключение клиента не остановит бесконечный цикл, и по умолчанию вообще не будет ответа — только бесконечная загрузка. Различные параметры тайм-аута (один из которых harakiri) могут в конечном итоге показывать тайм-аут соединения - например, php имеет (насколько я помню) тайм-аут по умолчанию 30 секунд, и он вернет 504 тайм-аут шлюза. Тайм-аут отключения сокета зависит от настроек http-сервера и не остановит поток приложения, а только закроет клиентский сокет.

Если не использовать gevent (или любые другие зеленые потоки), бесконечный цикл будет занимать 100% доступной мощности процессора (ограничено одним ядром), возможно, потребляя все больше и больше памяти, поэтому ваш сайт будет работать довольно медленно и / или таймаут очень быстрый. Сам Django не знает о времени запроса, поэтому, как упоминалось ранее, ваш стек рабочей среды — это способ предотвратить это. В случае uwsgi http://uwsgi-docs.readthedocs.org/en/latest/Options.html#harakiri-verbose — то, что вам нужно.

Harakiri печатает трассировку стека убитых процессов: (https://uwsgi-docs.readthedocs.org/en/latest/Tracebacker.html?highlight=harakiri) прямо в журнал uwsgi, а благодаря системе оповещения вы можете получать уведомления по электронной почте (http://uwsgi-docs.readthedocs.org/en/latest/AlarmSubsystem.html)

person Krzysztof Bujniewicz    schedule 28.04.2013
comment
Вариант Harakiri — это шаг в правильном направлении, потому что он предотвращает зависание сервера, но не помогает найти корень проблемы и устранить ее. Что бы я хотел, так это получить по электронной почте трассировку стека нарушителя, чтобы я мог проверить его и исправить проблему в коде. - person Ram Rachum; 29.04.2013
comment
Harakiri печатает трассировку стека и запрашивает информацию, а система сигнализации nginx позволяет получать уведомления по электронной почте. Обновленный ответ со ссылками. - person Krzysztof Bujniewicz; 29.04.2013

Я только что проверил это на сервере разработки Django.

Результаты:

  • Не дает таймаут через 30 секунд. (это может быть, потому что это не рабочий сервер)
  • Остается в загрузке, пока я не закрою страницу.

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

Может быть, что-то вроде:

import threading
from django.http import HttpResponse

class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
    def run(self):
        print "your possible infinite loop code here"

def possible_loop_view(request):
    thread = MyThread()
    thread.start()
    return HttpResponse("html response")
person ronniemagatti    schedule 27.04.2013
comment
На самом деле, теперь, когда я думаю, вы можете вызвать thread = MyThread() в другой функции, чтобы вы могли фактически получить к ней доступ и остановить ее позже ... но все же возможное решение? - person ronniemagatti; 27.04.2013
comment
Я действительно не понимаю, как ваш ответ решает что-либо. Во-первых, код должен завершиться до возврата ответа. Во-вторых, вы даже не показали, как созданный вами поток будет автоматически остановлен. - person Ram Rachum; 27.04.2013
comment
О, ты прав, я думаю, я не думал об этом так много. Мне жаль. - person ronniemagatti; 28.04.2013

Да, ваш анализ верен. Рабочий поток/процесс будет продолжать работать. Более того, если в цикле нет ожидания/сна, он будет нагружать ЦП. Другие потоки/процессы будут получать очень мало ресурсов процессора, что приведет к медленному отклику всего сайта.

Кроме того, я не думаю, что сервер явно отправит клиенту какую-либо ошибку тайм-аута. Если тайм-аут TCP установлен, TCP-соединение будет закрыто.

У клиента также может быть некоторый тайм-аут для получения ответа, который может появиться на картинке.

Избегать такого кода — лучший способ избежать такого кода. Вы также можете иметь на сервере какой-либо инструмент мониторинга, чтобы отслеживать использование ЦП/памяти и уведомлять о ненормальной активности, чтобы вы могли принять меры.

person Rohan    schedule 27.04.2013