问题描述
我设法编写了一个相当愚蠢的错误,它会使我的一个请求处理程序运行非常慢的数据库查询.
I managed to code a rather silly bug that would make one of my request handlers run a very slow DB query.
有趣的是,我注意到即使在攻城战完成很久之后的 Tornado 仍然在处理请求(有时是 90 年代之后).(评论 --> 我不是 100% 确定 Siege 的工作原理,但我相当确定它关闭了连接..)
Interesting bit is that I noticed that even long-after siege completed Tornado was still churning through requests (sometimes 90s later). (Comment --> I'm not 100% sure of the workings of Siege, but I'm fairly sure it closed the connection..)
我的问题分两部分:- 当客户端关闭连接时 Tornado 会取消请求处理程序吗?- 有没有办法在 Tornado 中超时请求处理程序?
My question in two parts:- Does Tornado cancel request handlers when client closes the connection?- Is there a way to timeout request handlers in Tornado?
我通读了代码,但似乎找不到任何东西.即使我的请求处理程序在上述 bug 中异步运行,待处理请求的数量也会增加到导致应用程序变慢的程度,最好关闭连接.
I read through the code and can't seem to find anything. Even though my request handlers are running asynchronously in the above bug the number of pending requests piled up to a level where it was slowing down the app and it would have been better to close out the connections.
推荐答案
当客户端断开连接时,Tornado 不会自动关闭请求处理程序.但是,您可以覆盖 on_connection_close
以在客户端断开时收到警报,这将允许您取消连接.上下文管理器(或装饰器)可用于设置处理请求的超时;使用 tornado.ioloop.IOLoop.add_timeout
来安排一些方法,该方法使请求在 timeout
之后超时运行,作为上下文的 __enter__
的一部分manager,然后在上下文管理器的 __exit__
块中取消该回调.下面是一个演示这两种想法的示例:
Tornado does not automatically close the request handler when the client drops the connection. However, you can override on_connection_close
to be alerted when the client drops, which would allow you to cancel the connection on your end. A context manager (or a decorator) could be used to handle setting a timeout for handling the request; use tornado.ioloop.IOLoop.add_timeout
to schedule some method that times out the request to run after timeout
as part of the __enter__
of the context manager, and then cancel that callback in the __exit__
block of the context manager. Here's an example demonstrating both of those ideas:
import time
import contextlib
from tornado.ioloop import IOLoop
import tornado.web
from tornado import gen
@gen.coroutine
def async_sleep(timeout):
yield gen.Task(IOLoop.instance().add_timeout, time.time() + timeout)
@contextlib.contextmanager
def auto_timeout(self, timeout=2): # Seconds
handle = IOLoop.instance().add_timeout(time.time() + timeout, self.timed_out)
try:
yield handle
except Exception as e:
print("Caught %s" % e)
finally:
IOLoop.instance().remove_timeout(handle)
if not self._timed_out:
self.finish()
else:
raise Exception("Request timed out") # Don't continue on passed this point
class TimeoutableHandler(tornado.web.RequestHandler):
def initialize(self):
self._timed_out = False
def timed_out(self):
self._timed_out = True
self.write("Request timed out!\n")
self.finish() # Connection to client closes here.
# You might want to do other clean up here.
class MainHandler(TimeoutableHandler):
@gen.coroutine
def get(self):
with auto_timeout(self): # We'll timeout after 2 seconds spent in this block.
self.sleeper = async_sleep(5)
yield self.sleeper
print("writing") # get will abort before we reach here if we timed out.
self.write("hey\n")
def on_connection_close(self):
# This isn't the greatest way to cancel a future, since it will not actually
# stop the work being done asynchronously. You'll need to cancel that some
# other way. Should be pretty straightforward with a DB connection (close
# the cursor/connection, maybe?)
self.sleeper.set_exception(Exception("cancelled"))
application = tornado.web.Application([
(r"/test", MainHandler),
])
application.listen(8888)
IOLoop.instance().start()
这篇关于“超时"的正确方法龙卷风中的请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!