V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
jianhaochende
V2EX  ›  Python

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

  •  
  •   jianhaochende · 2021-10-05 12:05:31 +08:00 · 2389 次点击
    这是一个创建于 1137 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近对比了各种 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()})
    
    10 条回复    2021-10-08 22:59:53 +08:00
    abersheeran
        1
    abersheeran  
       2021-10-05 21:26:42 +08:00   ❤️ 1
    GitHub 上向我们提问的原来是你……这个扩展是我设计的,设计之初我想的是追求极致的性能——用 C 、Rust 实现的服务器接受一个文件描述符远比接受一个 PyObject 要更方便处理。

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

    说来当初弄这个原因就是想搞个完美的 ASGI 服务器,也是看着一堆拉胯的 ASGI 服务器为 python 着急,ASGI 框架的性能比的就是 ASGI 服务器的性能,然而官方的实现并不怎么出色。Daphne,自称是 ASGI 的参考实现,快 3 年了,lifespan 连影子都没有,但依赖一大堆,又慢又重。uvicorn 对 http/2 不感兴趣也是他们作者说的,看起来还算有希望的就剩 hypercorn 和 nginx unit 了。hypercorn 功能最多,基本上完整实现了 ASGI 规范的全部东西,简直是 ASGI 的希望,unit 是 nginx 推动的,纯 C 实现,速度比 uvicorn 还快,也是未来的希望之一。真心希望这几个服务器能发展起来,也算是对异步生态的贡献了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2643 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 15:32 · PVG 23:32 · LAX 07:32 · JFK 10:32
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.