【问题标题】:How to stop request processing in Cherrypy?如何停止 Cherrypy 中的请求处理?
【发布时间】:2014-02-27 03:26:15
【问题描述】:

我使用 python 2.6,cherrypy 3.1

我对请求超时有一些问题。我只需要在限制(30 秒)内完成请求。在该限制之后,应终止进程并返回答案

服务器正在通过 tree.mount();樱桃py.start(); cherrypy.block()

首先...当我尝试杀死应用程序(通过 Ctrl+C (debian 6.0))时,应用程序卡在:

等待子线程终止...

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

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

马丁你好

【问题讨论】:

    标签: python cherrypy


    【解决方案1】:

    从 CherryPy 12 开始,_TimeoutMonitor 被删除。如果您仍然倾向于尝试下面的 hack,请参阅 the pull request 获取其来源。


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

    在 Python 中杀死一个线程

    一般你做不到。只是没有这样的 API。在某些情况下您可以找到some hacks,但一般情况下您找不到。终止线程的唯一稳定方法是通过标志、事件等与之合作。

    强制停止 CherryPy 进程

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

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

    处理可能无响应的任务

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

    如果您可以在执行处理或计算的最后限制执行,您最好在那里执行。无论是数据库,还是 Web 服务 API 调用,或者诸如此类,然后只处理超时异常。

    CherryPy 开箱即用响应超时

    CherryPy 有开箱即用的响应超时功能。它由response.timeout 配置和cherrypy._TimeoutMonitor 控制,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 线程。这是 Python 文档中对 Process.terminate 的引用。

    如果在关联进程使用管道或队列时使用此方法,则管道 或者队列很容易被损坏并且可能变得无法被其他进程使用。 同样,如果进程获得了锁或信号量等,则终止它是 容易导致其他进程死锁。

    所以是的,强制超时在技术上是可行的,但这是一种不鼓励且容易出错的方式。

    【讨论】:

    • 2019 年的警告:response.timeout 不再存在,而且它并没有按照您认为的方式工作。请参阅:github.com/cherrypy/cherrypy/issues/1625 不要浪费时间尝试完成这项工作!
    猜你喜欢
    • 2015-11-15
    • 2015-07-13
    • 2021-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    相关资源
    最近更新 更多