我正在浏览 pythonasyncio
今晚我正在查看模块文档,为我的一个课程项目寻找一些想法,但我很快发现 python 标准中可能缺少功能aysncio
module.
如果你查看文档,你会发现有一个基于回调的 API 和一个基于协程的 API。回调 API 可以用于构建 UDP 和 TCP 应用程序,而协程 API 看起来只能用于构建 TCP 应用程序,因为它使用了流式 API。
这对我来说确实是一个问题,因为我正在寻找一个基于协程的 UDP 网络 API,尽管我确实发现了asyncio
支持基于低级协程的套接字方法,例如sock_recv https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.sock_recv and sock_sendall https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.sock_sendall,但是 UDP 网络的关键 API,recvfrom
and sendto
不在那里。
我想做的是编写一些代码,例如:
async def handle_income_packet(sock):
await data, addr = sock.recvfrom(4096)
# data handling here...
await sock.sendto(addr, response)
我知道这可以使用回调 API 等效地实现,但这里的问题是回调不是协程而是常规函数,因此在其中您无法将控制权交还给事件循环并保留函数执行状态。
看看上面的代码,如果我们需要在数据处理部分做一些阻塞IO操作,那么只要我们的IO操作也在协程中完成,那么在协程版本中就不会有问题:
async def handle_income_packet(sock):
await data, addr = sock.recvfrom(4096)
async with aiohttp.ClientSession() as session:
info = await session.get(...)
response = generate_response_from_info(info)
await sock.sendto(addr, response)
只要我们使用await
事件循环将从该点开始控制流来处理其他事情,直到 IO 完成。但遗憾的是这些代码是not目前可用,因为我们没有协程版本socket.sendto
and socket.recvfrom
in asyncio
.
我们可以使用传输协议回调 API 来实现这一点:
class EchoServerClientProtocol(asyncio.Protocol):
def connection_made(self, transport):
peername = transport.get_extra_info('peername')
self.transport = transport
def data_received(self, data):
info = requests.get(...)
response = generate_response_from_info(info)
self.transport.write(response)
self.transport.close()
我们不可以await
那里有一个协程,因为回调不是协程,并且使用像上面这样的阻塞 IO 调用会阻止回调中的控制流,并阻止循环处理任何其他事件,直到 IO 完成
另一个推荐的实现想法是创建一个Future
对象在data_received
函数,将其添加到事件循环中,并将任何需要的状态变量存储在 Protocol 类中,然后显式地将控制权返回给循环。虽然这可行,但它确实创建了许多复杂的代码,而在协程版本中,它们根本不需要。
Also here https://www.pythonsheets.com/notes/python-asyncio.html#simple-asyncio-udp-echo-server我们有一个使用非阻塞套接字的示例add_reader
用于处理 UDP 套接字。但与 coroutine-version 的几行代码相比,代码看起来仍然很复杂。
我想说的一点是,协程是一种非常好的设计,可以利用单线程中的并发能力,同时还具有非常简单的设计模式,可以节省脑力和不必要的代码行,但关键部分是要获得我们确实缺乏它适用于 UDP 网络asyncio
标准库。
大家对此有何看法?
另外,如果对支持此类 UDP 网络 API 的 3rd 方库有任何其他建议,我将非常感谢我的课程项目。我发现Bluelet https://github.com/sampsyo/bluelet很像这样的事情,但似乎并没有积极维护。
edit:
看来这PR https://github.com/python/asyncio/pull/321确实实现了这个功能,但被拒绝了asyncio
开发商。开发者声称所有功能都可以使用来实现create_datagram_endpoint()
,协议传输 API。但正如我上面所讨论的,在许多用例中,与使用回调 API 相比,协程 API 具有简单性的优势,但不幸的是我们在 UDP 中没有这些。