龙卷风:在异步打开中捕获异常

Tornado: catching exceptions inside asynchronous open

我对 WebSocketHandler 的方法 open 有疑问。 我用 gen.coroutine 包装它以便在内部使用异步调用(访问 redis)。但是遇到了另一个问题,open里面的任何错误都没有捕获。

示例:

@gen.coroutine
def open(self):
    t = 8/0
    self._connection_id = yield self._generate_connection_id()
    self.write_message('...')

方法open在WebSocketProtocol._run_callback内部调用:

def _run_callback(self, callback, *args, **kwargs):
    try:
        callback(*args, **kwargs)
    except Exception:
        app_log.error("Uncaught exception in %s",
                      self.request.path, exc_info=True)
        self._abort()

此方法没有任何装饰器,因此方法打开 return future 且此异常未处理。

那么如何在 open 中使用异步方法,并处理异常?

作为一般规则,协程只能被其他协程正确调用,所以当重写基础 class 的方法时,如 WebSocketHandler.open,除非该方法是协程或被记录为"may be a coroutine"或者"may return a Future",做成协程是不安全的。 WebSocketHandler.open 可能不是协程(从 Tornado 4.1 开始)。

您可以使用 IOLoop.spawn_callback:

从 open() 生成协程
def open(self):
    IOLoop.current().spawn_callback(self.async_open)

@gen.coroutine
def async_open(self):
    #...

async_open 中,您可以像往常一样使用 try/except 处理错误(spawn_callback 也会为您记录异常,作为最后的手段)。您也有责任处理 async_openon_messageon_close 之间可能出现的任何时间问题,因为这些方法可能会在 async_open 完成之前被调用。