如何在Cherrypy中停止请求处理?

时间:2014-02-03 15:11:54

标签: python cherrypy

即时通讯使用python 2.6,cherrypy 3.1

我的请求超时有问题。我只需要在Limit(30秒)内完成请求。在该限制之后,应该杀死进程并回答

服务器从tree.mount()开始; cherrypy.start(); cherrypy.block()

作为第一件事......当我试图杀死应用程序时(通过Ctrl + C(debian 6.0)),应用程序停留在:

等待子线程终止...

如何在退出时终止进程以及如何处理超时连接以杀死未响应的进程?

我不能在这里写任何代码,因为它是严格专有的,无论如何,我希望有人已经解决了这个问题。

问候马丁

1 个答案:

答案 0 :(得分:3)

<强>注意即可。如果您在正常执行流程中不小心杀死线程或进程,那么很可能会搞砸。如果Python代码exceptfinally不能执行,您将遇到内存和同步问题(例如死锁),未关闭的文件句柄和套接字等等等等等等。 杀戮是解决您的问题的方法,就像断头台可能被称为治疗头皮屑一样

用Python杀死一个线程

一般来说,你不能这样做。没有这样的API。对于某些情况,您可以找到some hacks,但一般情况下您无法找到。{3}}。终止线程的唯一稳定方法是通过标志,事件等与之合作。

强制停止CherryPy过程

当您在拥有CherryPy Python进程的终端中按 Ctrl + C 时,解释器会收到SIGINT信号并引发KeyboardInterrupt异常。然后CherryPy命令其工作线程停止并告诉您等待子线程终止... 。如果在用户代码中阻止了工作线程,CherryPy将一直等到它被释放。

要强制停止,您可以使用常见的kill -9 PID,其中PID是您的CherryPy流程的进程ID。有时您可以使用任何过程监视器找到它。或者合并cherrypy.process.plugins.PIDFile来编写流程的pid文件。

处理可能无响应的任务

一般来说,CherryPy是一个线程服务器。如果您的任务有十几秒的响应,则很容易耗尽工作线程。在这种情况下,后台任务队列可能是一个好主意(CeleryRq)或至少使用cherrypy.process.plugins.BackgroundTask。但它显然会让您重新设计系统,进行临时结果存储,响应轮询或推送。它增加了复杂性,因此应该对所有利弊进行权衡。

如果您可以限制处理或计算的最终执行,那么您最好在那里执行。无论是数据库,还是Web服务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.Queuemultiprocessing.Pipe与您的工作进程进行通信,因为当它被杀死时,访问其中任何一个都会锁定您的CherryPy线程。以下是Process.terminate的Python文档的引用。

  

如果关联进程使用管道或队列,则使用此方法,然后使用管道   或队列容易被破坏,并可能被其他进程无法使用。   类似地,如果进程已获得锁或信号量等,则终止它   可能导致其他进程陷入僵局。

所以是的,技术上可以强制超时,但这是一种沮丧和容易出错的方式。