【发布时间】:2018-01-02 03:05:51
【问题描述】:
由于我的项目严重依赖于异步网络 I/O,我总是不得不预期会发生一些奇怪的网络错误:无论是我连接的服务出现 API 中断,还是我自己的服务器出现网络问题, 或者是其他东西。这样的问题出现了,并且没有真正的解决方法。因此,我最终试图找出一种方法,以在发生此类网络问题时从外部有效地“暂停”协程的执行,直到重新建立连接。我的方法是编写一个装饰器pausable,它接受一个参数pause,这是一个协程函数,它将是yielded from / awaited,如下所示:
def pausable(pause, resume_check=None, delay_start=None):
if not asyncio.iscoroutinefunction(pause):
raise TypeError("pause must be a coroutine function")
if not (delay_start is None or asyncio.iscoroutinefunction(delay_start)):
raise TypeError("delay_start must be a coroutine function")
def wrapper(coro):
@asyncio.coroutine
def wrapped(*args, **kwargs):
if delay_start is not None:
yield from delay_start()
for x in coro(*args, **kwargs):
try:
yield from pause()
yield x
# catch exceptions the regular discord.py user might not catch
except (asyncio.CancelledError,
aiohttp.ClientError,
websockets.WebSocketProtocolError,
ConnectionClosed,
# bunch of other network errors
) as ex:
if any((resume_check() if resume_check is not None else False and
isinstance(ex, asyncio.CancelledError),
# clean disconnect
isinstance(ex, ConnectionClosed) and ex.code == 1000,
# connection issue
not isinstance(ex, ConnectionClosed))):
yield from pause()
yield x
else:
raise
return wrapped
return wrapper
特别注意这一点:
for x in coro(*args, **kwargs):
yield from pause()
yield x
使用示例(ready 是 asyncio.Event):
@pausable(ready.wait, resume_check=restarting_enabled, delay_start=ready.wait)
@asyncio.coroutine
def send_test_every_minute():
while True:
yield from client.send("Test")
yield from asyncio.sleep(60)
但是,这似乎不起作用,对我来说似乎不是一个优雅的解决方案。是否有与 Python 3.5.3 及更高版本兼容的有效解决方案?兼容 Python 3.4.4 及更高版本是可取的。
附录
只是try/excepting 需要暂停的协程中引发的异常对我来说既不可行也不可行,因为它严重违反了我想要的核心代码设计原则(DRY)遵从;换句话说,在这么多协程函数中排除这么多异常会让我的代码变得混乱。
【问题讨论】:
标签: python python-3.x asynchronous concurrency python-asyncio