【问题标题】:How to catch write aborts in python-tornado?如何在 python-tornado 中捕获写入中止?
【发布时间】:2013-10-30 08:58:54
【问题描述】:

我想通过 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 永远不会被调用。

底线 - 仍然不起作用。

【问题讨论】:

    标签: python tornado


    【解决方案1】:

    您需要使处理程序异步并使用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()
    

    【讨论】:

    • 我明白了...我需要不时将控制权传递给 ioloop 以使 on_connection_close 工作。在我的原始示例中,我正在获取光标,所以我想我需要以异步方式进行。不幸的是,这并不方便。我希望 self.write 在 SIGPIPE 的情况下会调用 on_connection_close。感谢您的详细示例。
    • 我想我需要以异步方式进行 — 是的
    【解决方案2】:

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

    【讨论】:

    • 试过这个并没有帮助 - 请查看更新的答案。谢谢。
    猜你喜欢
    • 2012-08-14
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多