Как остановить обработку запросов в Cherrypy?
Я использую python 2.6, cherrypy 3.1
У меня есть некоторые проблемы с таймаутом запросов. Мне просто нужно, чтобы запросы выполнялись в Limit (30 секунд). после этого предела процессы должны быть убиты, а ответ возвращен
сервер запускается через tree.mount(); cherrypy.start(); cherrypy.block()
во-первых... когда я пытаюсь убить приложение (с помощью Ctrl+C (debian 6.0)), приложение зависает:
Ожидание завершения дочерних потоков...
как убить процессы при выходе и как обработать тайм-аут соединения для уничтожения процесса, который не отвечает?
Я не могу написать здесь какой-либо код, потому что он строго проприетарный, так или иначе, я надеюсь, что кто-то уже решил эту проблему.
Привет Мартин
1 ответ
Осторожно Весьма вероятно, что вы все испортите, если будете небрежно убивать потоки или процессы как часть обычного потока выполнения. В случае кода Python except
а также finally
не будет выполняться, у вас будут проблемы с памятью и синхронизацией (например, взаимоблокировки), незакрытые файловые дескрипторы и сокеты, и так далее, и так далее. Убийство - это решение вашей проблемы, так же как гильотину можно назвать средством от перхоти.
Уничтожение потока в Python
В общем, вы не можете сделать это. Там просто нет такого API. Вы можете найти некоторые хаки для некоторых случаев, но в целом вы не можете. Единственный стабильный способ завершить поток - это сотрудничать с ним с помощью флагов, событий и т. Д.
Принудительно остановить процесс CherryPy
Когда вы нажимаете Ctrl + C в терминале, который владеет процессом CherryPy Python, интерпретатор получает SIGINT
сигнал и поднимает KeyboardInterrupt
исключение. Затем CherryPy приказывает своим рабочим потокам остановиться и сообщает " Ожидание завершения дочерних потоков...". Если рабочий поток заблокирован в пользовательском коде, CherryPy будет ждать его освобождения.
Для принудительной остановки вы можете использовать общие kill -9 PID
, где PID
это идентификатор процесса вашего CherryPy. В некоторых случаях вы можете найти его на любом мониторе процесса. Или включить cherrypy.process.plugins.PIDFile
написать pid-файл процесса.
Обработка потенциально не отвечающих задач
В общем, CherryPy - это многопоточный сервер. Если для ваших задач характерен десятоксекундный ответ, вы можете легко исчерпать рабочие потоки. В таком случае хорошей задачей может быть очередь фоновых задач ( Celery, Rq) или, по крайней мере, некоторое использование cherrypy.process.plugins.BackgroundTask
, Но это, очевидно, заставляет вас перепроектировать вашу систему, сделать временное хранилище результатов, опрос ответов или отправку. Это добавляет сложности, поэтому решение должно быть принято с учетом всех плюсов и минусов.
Если вы можете ограничить выполнение в конце, который выполняет обработку или вычисление, лучше сделать это там. Будь то база данных, вызов API веб-службы или еще много чего, а затем просто обработайте исключение тайм-аута.
Время ожидания ответа CherryPy "из коробки"
В CherryPy есть встроенная функция ожидания ответа. Контролируется response.timeout
конфигурация и cherrypy._TimeoutMonitor
который выполняется в отдельном потоке и смотрит, истек ли срок ответа. Хотя на самом деле монитор только устанавливает атрибут, response.timed_out
, который позже рассматривается в cherrypy._cprequest.Request.run
и если это правда cherrypy.TimeoutError
Поднялся. Таким образом, исключение тайм-аута поднимается постфактум. Если ваш обработчик страниц блокируется на 30 секунд, вы получите исключение только через 30 секунд.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
# interval in seconds at which the timeout monitor runs
'engine.timeout_monitor.frequency' : 1
},
'/' : {
# the number of seconds to allow responses to run
'response.timeout' : 2
}
}
class App:
@cherrypy.expose
def index(self):
time.sleep(8)
print('after sleep')
return '<em>Timeout test</em>'
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
Принудительное время ожидания ответа
Вы не можете убить поток, но вы можете убить процесс. Если вы не можете управлять своими задачами любым другим способом, вы можете свернуть выполнение в процессе и использовать монитор, чтобы убить его, когда закончится время. Следующее демонстрирует идею.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import signal
import time
from multiprocessing import Process
import cherrypy
config = {
'global' : {
'server.socket_host' : '127.0.0.1',
'server.socket_port' : 8080,
'server.thread_pool' : 8,
# disable internal timeout monitor
'engine.timeout_monitor.on' : False,
# interval in seconds at which the timeout monitor runs
'engine.timeout_kill_monitor.frequency' : 1
},
'/' : {
# the number of seconds to allow responses to run
'response.timeout' : 2
}
}
class TimeoutKillMonitor(cherrypy._TimeoutMonitor):
def run(self):
cherrypy._TimeoutMonitor.run(self)
for request, response in self.servings:
if response.timed_out and hasattr(response, 'jobProcess'):
if response.jobProcess.is_alive():
os.kill(response.jobProcess.pid, signal.SIGKILL)
cherrypy.engine.timeout_kill_monitor = TimeoutKillMonitor(cherrypy.engine)
cherrypy.engine.timeout_kill_monitor.subscribe()
def externalJob():
time.sleep(8)
class App:
@cherrypy.expose
def index(self):
p = Process(target = externalJob)
p.start()
cherrypy.response.jobProcess = p
p.join()
# To determine whether your job process has been killed
# you can use ``killed = response.timed_out``. Reset it to
# ``False`` to avoid ``TimeoutError`` and response a failure
# state in other way.
return '<em>Result</em>'
if __name__ == '__main__':
cherrypy.quickstart(App(), '/', config)
Обратите внимание, что вы не можете использовать multiprocessing.Queue
или же multiprocessing.Pipe
для связи с вашим рабочим процессом, потому что при его завершении доступ к любому из них заблокирует ваш поток CherryPy. Вот цитата из документации по Python для Process.terminate
,
Если этот метод используется, когда связанный процесс использует канал или очередь, то канал или очередь могут быть повреждены и могут стать непригодными для использования другим процессом. Точно так же, если процесс получил блокировку или семафор и т. Д., То его завершение может привести к взаимоблокировке других процессов.
Так что да, технически возможно форсировать таймаут, но это обескураженный и подверженный ошибкам способ.