Python 的 ASGI 服务器在调用 loop.sendfile 时客户端无法收到数据

2021-10-05 12:05:31 +08:00
 jianhaochende

最近对比了各种 ASGI 服务器,发现当前的几个 ASGI 服务器中 ,hypercorn 的完成度是最高的,http 支持到 3,ASGI 扩展也支持了两个,可惜 Zero Copy Send 目前还没支持,当时就想对他加入这个的支持,如果成功,这也是第一个支持全部 ASGI 扩展的 ASGI 服务器,然而 hypercorn 原作者似乎最近没空,我就自己 fork 了个,主要代码在这,https://gitlab.com/synodriver/hypercorn/-/blob/zerocopy/src/hypercorn/asyncio/tcp_server.py#L106, 但是测试中发生了问题,在 debian 上 Errno32 Brokenpipe,在 win 上客户端也是收不到数据,似乎一旦调用 loop.sendfile,对面就关闭了连接。有对 ASGI 协议和服务器有研究的大佬可以帮忙看看吗?

这是我用的测试 code

async def app2(scope, receive, send):
    if scope["type"] == "http" and scope["path"] == "/":
        await send({"type": "http.response.start", "status": 200,
                    "headers": [(b"Content-Type", b"image/png"), (b"Cache-Control", b"no-cache")]})
        f = open(r"test.jpg", "rb")
        await send({"type": "http.response.zerocopysend", "file": f.fileno()})
2422 次点击
所在节点    Python
10 条回复
abersheeran
2021-10-05 21:26:42 +08:00
GitHub 上向我们提问的原来是你……这个扩展是我设计的,设计之初我想的是追求极致的性能——用 C 、Rust 实现的服务器接受一个文件描述符远比接受一个 PyObject 要更方便处理。

这里你应该用 os.sendfile 而不是 loop.sendfile
abersheeran
2021-10-05 22:38:28 +08:00
看到你这个帖子之后我想起来之前 uvicorn 的维护者问我有没有空帮忙实现这个扩展,刚刚写了一下 https://github.com/encode/uvicorn/pull/1210 。你参考参考吧。
jianhaochende
2021-10-05 23:58:16 +08:00
感谢回复。我给 hypercorn 用上后,显示是已经发送成功了,但是浏览器并没有收到,发送文件过大时甚至出现 lockingIOError: [Errno 11] Resource temporarily unavailable,很迷惑。hypercorn 只有 h11 h2 h3 的解析器,没有 httptools 的
jianhaochende
2021-10-06 00:01:17 +08:00
haoliang
2021-10-06 00:21:47 +08:00
@abersheeran 这个 pr 中 `async send(): ... os.sendfile()`, 看起来一旦有一个 request 的 coroutine 中触发了 `os.sendfile` 那么整个程序的其他 coroutine 就一直 block ?
或许是我漏看了哪里,我之前也在异步程序中使用 `os.sendfile` 当时是借助单独的线程(池)
abersheeran
2021-10-06 02:20:15 +08:00
@haoliang os.sendfile 不会阻塞。
jianhaochende
2021-10-06 11:57:19 +08:00
@abersheeran 请问使用 h11 的不能实现是和 h11 自身的状态机有关吗?
abersheeran
2021-10-06 14:30:25 +08:00
@jianhaochende 对。我想了一下,如果你真要在 h11 里实现,只能在 sendfile 之后生成等长度的空白 bytes 丢给 h11,性能会差很多,不过还是要比一点点读文件去发送要快。你可以试试。
haoliang
2021-10-07 13:50:48 +08:00
有个 h11.Connection.send_with_data_passthrough 方法,不需要构造等长度的数据块
* https://h11.readthedocs.io/en/latest/api.html#h11.Connection.send_with_data_passthrough
jianhaochende
2021-10-08 22:59:53 +08:00
感谢,目前已经在 hypercorn 中实现了。

说来当初弄这个原因就是想搞个完美的 ASGI 服务器,也是看着一堆拉胯的 ASGI 服务器为 python 着急,ASGI 框架的性能比的就是 ASGI 服务器的性能,然而官方的实现并不怎么出色。Daphne,自称是 ASGI 的参考实现,快 3 年了,lifespan 连影子都没有,但依赖一大堆,又慢又重。uvicorn 对 http/2 不感兴趣也是他们作者说的,看起来还算有希望的就剩 hypercorn 和 nginx unit 了。hypercorn 功能最多,基本上完整实现了 ASGI 规范的全部东西,简直是 ASGI 的希望,unit 是 nginx 推动的,纯 C 实现,速度比 uvicorn 还快,也是未来的希望之一。真心希望这几个服务器能发展起来,也算是对异步生态的贡献了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/805936

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX