我想用三重奏实现一个服务器。单个客户端连接由托儿所产生的任务处理。然而,三位文档说“如果托儿所内的任何任务以未处理的异常结束,那么托儿所会立即取消托儿所内的所有任务。”。这对我的用例来说是非常不幸的。我宁愿在记录错误时继续为其他连接提供服务。有没有办法做到这一点?
问问题
428 次
1 回答
3
你可以自己实现 Nursery 接口:
class ExceptionLoggingNursery:
def __init__(self, nursery):
self.nursery = nursery
@property
def cancel_scope(self):
return self.nursery.cancel_scope
async def _run_and_log_errors(self, async_fn, *args):
# This is more cumbersome than it should be
# See https://github.com/python-trio/trio/issues/408
def handler(exc):
if not isinstance(exc, Exception):
return exc
logger.error("Unhandled exception!", exc_info=exc)
with trio.MultiError.catch(handler):
return await async_fn(*args)
def start_soon(self, async_fn, *args, **kwargs):
self.nursery.start_soon(self._run_and_log_errors, async_fn, *args, **kwargs)
async def start(self, async_fn, *args, **kwargs):
return await self.nursery.start(self._run_and_log_errors, async_fn, *args, **kwargs)
@asynccontextmanager
async def open_exception_logging_nursery():
async with trio.open_nursery() as nursery:
yield ExceptionLoggingNursery(nursery)
请注意,我们只捕获Exception
子类,并允许其他异常继续传播。这意味着如果您的一个子任务引发了一个KeyboardInterrupt
(因为您按下了 control-C),或者一个trio.Cancelled
(因为您,嗯,取消了它......也许是因为您按下了 control-C 并且父母居住的托儿所得到了取消),那么这些异常被允许传播出去并且仍然导致所有其他任务被取消,这几乎可以肯定是你想要的。
这是一些代码,但它可以很容易地放入可重用的库中。(如果我真的这样做,我可能会将异常处理代码作为传递给的参数open_exception_logging_nursery
,而不是对调用的硬编码logger.error
。)而且我很想看到一个包含这种“智能主管”的库——基本的三重奏托儿所总是作为这些东西的基石。您也可以想象其他更有趣的策略,例如“如果任务以未处理的异常退出,则记录一些内容然后重新启动它,并以指数退避”。(Erlang 主管是一个很好的想法来源偷受到启发。)
于 2018-01-20T11:03:58.860 回答