2013-04-27 2 views
7

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

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

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

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

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

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

Действительно ли это настоящая проблема? Есть ли способ избежать этого?

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

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

+2

интересно читать: HTTP: // StackOverflow. com/questions/8685695/how-do-i-run-long-term-infin-python-процессы –

+0

Я обновил свой ответ, надеюсь, теперь он ответит на ваш вопрос :) –

ответ

4

Это реальная проблема. В случае 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)

+0

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

+0

Harakiri выполняет печать трассировки и запроса информации, а система сигнализации nginx позволяет получать уведомления по электронной почте. Обновленный ответ со ссылками. –

+0

К сожалению, я имел в виду систему сигнализации uwsgi, конечно же :) –

0

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

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

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

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

2

Я только что протестировал это на сервере разработки 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") 
+0

На самом деле, теперь, когда я думаю, вы может потребоваться вызвать thread = MyThread() в другой функции, чтобы вы могли получить к ней доступ и остановить его позже .. но все же возможное решение? – Ramalus

+0

Я действительно не понимаю, как ваш ответ решает что угодно. Во-первых, код должен заканчиваться * перед тем, как возвращается ответ. Во-вторых, вы даже не показывали, как созданный поток будет автоматически остановлен. –

+0

О, ты прав, я думаю, я так не думал об этом. Прости. – Ramalus