How to stop request processing on Cherrypy?

im using python 2.6, cherrypy 3.1

I have a problem with request timeout. I just need to make requests in Limit (30 seconds). after this limit processes must be killed and the response will be returned

the server is launched through tree.mount (); cherrypy.start (); cherrypy.block ()

as the first thing ... when I try to kill the application (Ctrl + C (debian 6.0)), the application is stuck:

Waiting for child threads to complete ...

how to kill processes on exit and how to handle a connection with a timeout for killin a process that does not respond?

I cannot write any code here because it is strictly proprietary, in any case, I hope that someone else has solved this problem.

Greetings Martin

+2
source share
1 answer

Attention It is very likely that you will ruin things if you carelessly kill threads or processes as part of a regular execution thread. In the event that Python except and finally code fails, you will have memory and synchronization problems (for example, deadlocks), open file descriptors and sockets, etc. Etc. Killing is the solution to your problem, as the guillotine may be called the cure for dandruff .

Killing a thread in Python

In general, you cannot do this . There is simply no such API. You can find some hacks for some cases, but in general you cannot. The only stable way to stop the flow is to cooperate with it on flags, events, etc.

Force-stop CherryPy Process

When you press Ctrl + C in the terminal that owns the CherryPy Python process, the interpreter receives a SIGINT signal and throws a KeyboardInterrupt exception. Then CherryPy commands its workflows to stop and tells you: "Waiting for the end of the child threads ..." If the workflow is blocked in the user code, CherryPy will wait until it is released.

To force a stop, you can use the common kill -9 PID , where PID is the process ID of your CherryPy process. Sometimes you can find it using any process monitor. Or include cherrypy.process.plugins.PIDFile to write the pidfile of the process.

Handling Potentially Unresponsive Tasks

In general, CherryPy is a multi-threaded server. If your tasks use a dozen seconds, you can easily start workflows. In this case, a background task task may be a good idea ( Celery , Rq ) or at least some use of cherrypy.process.plugins.BackgroundTask . But this, obviously, forces you to redesign your system, make a temporary storage of the results, poll the poll or click. This adds complexity, so a decision must be made to weigh all the pros and cons.

If you can restrict execution to the end that processes or calculates, you better do that. Whether it's a database, or a web service API, or something else, then just handle the timeout exception.

CherryPy Response Timeout

CherryPy has a response timeout function. It is controlled by response.timeout and cherrypy._TimeoutMonitor , which runs in a separate thread and checks if the response has been delayed. Although, in fact, the monitor sets only the response.timed_out attribute, which is later considered in cherrypy._cprequest.Request.run , and if it is true, cherrypy.TimeoutError . Thus, a timeout exception occurs post factum . If your page handler blocks for 30 seconds, you will get an exception after only 30 seconds.

 #!/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) 

Force Response Response

You cannot kill a thread, but you can kill a process. If you cannot control your tasks in any other way, you can complete the execution process and use the monitor to kill it when it runs out of time. The following demonstrates this idea.

 #!/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) 

Please note that you cannot use multiprocessing.Queue or multiprocessing.Pipe to communicate with your workflow, because when it is killed, access to any of them will block your CherryPy stream. Here is a quote from the Python documentation for Process.terminate .

If this method is used when a related process uses a channel or queue, then the pipe or queue may become damaged and may become unusable by another process. Similarly, if a process has acquired a lock or semaphore, etc., then the completion of this which may lead to deadlock of other processes.

So, it is technically possible to force a timeout, but this discourages and error-prone the way.

+3
source

All Articles