如何捕捉python-tornado中的写入中止?

时间:2013-10-30 08:58:55

标签: python tornado

我想通过Tornado流式传输长数据库结果集。 我显然需要一个服务器游标,因为它不可能将整个查询加载到内存中。

所以我有以下代码:

class QueryStreamer(RequestHandler):

    def get(self):
      cursor.execute("Select * from ...")
      chunk = cursor.fetch(1000)
      while chunk:
          self.write(chunk)
          self.flush()
          chunk = cursor.fetch(1000)        
     self.finish()
     cursor.close()

如果有人直到最后才读到我的请求? (即curl ... |head), get方法很乐意将数据流式传输到任何地方。我希望在某些时候获得SIGPIPE并关闭数据库游标(不用将其运行到最后)。

如何在Tornado中捕获写入错误?

更新:根据答案中的建议我尝试了以下内容:

import tornado.ioloop
import tornado.web
import time

class PingHandler(tornado.web.RequestHandler):
        def get(self):
                for i in range(600):
                        self.write("pong\n")
                        self.flush()
                        time.sleep(1)
                        print "pong"
                self.finish()
                print "ponged"

        def on_connection_close(self):
                print "closed"

if __name__ == "__main__":
        application = tornado.web.Application([ ("/ping", PingHandler), ])
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()

我在终端1和终端2中运行此文件,我调用:

curl -s   http://localhost:8888/ping

在第一次回复后我点击了CTRL-C。但是在终端1中我看到它很高兴地保持“pong”-ing而on_connection_close永远不会被调用。

底线 - 仍然不起作用。

2 个答案:

答案 0 :(得分:7)

您需要使处理程序异步并使用ioloop.add_timeout而不是time.sleep,因为这会阻止循环:

import tornado.ioloop
import tornado.web
import tornado.gen


class PingHandler(tornado.web.RequestHandler):

    connection_closed = False

    def on_connection_close(self):
        print "closed"
        self.connection_closed = True

    @tornado.gen.coroutine  # <= async handler
    def get(self):

        for i in range(600):

            if self.connection_closed:
                # `on_connection_close()` has been called,
                # break out of the loop
                break

            self.write("pong %s\n" % i)
            self.flush()

            # Add a timeout. Similar to time.sleep(1), but non-blocking:
            yield tornado.gen.Task(
                tornado.ioloop.IOLoop.instance().add_timeout,
                tornado.ioloop.IOLoop.instance().time() + 1,
            )

        self.finish()
        print "finished"

if __name__ == "__main__":
    application = tornado.web.Application([("/ping", PingHandler), ])
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

答案 1 :(得分:2)

实现on_connection_close方法并让它在get处理程序中停止写入循环。