Как остановить обработку запросов в 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,

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

Так что да, технически возможно форсировать таймаут, но это обескураженный и подверженный ошибкам способ.

Другие вопросы по тегам