asgi-webdav icon indicating copy to clipboard operation
asgi-webdav copied to clipboard

support sendfile extension in asgi spec

Open synodriver opened this issue 4 years ago • 24 comments

In the current asgi spec, sendfile is supported. If this webdav server can take advantage of zero-copy-send extension, it would greatly improve server's performance.

当前的ASGI规范中,有个sendfile扩展。如果可以利用这个扩展直接发送文件,那么整个服务器的性能必然可以有很大的提升。

By the way, is there any way to fix that warnings? Iooks like the server send wrong status code.

顺便这有其他可以解决test套件warning的办法吗?看上去就是返回的状态码问题

synodriver avatar Dec 14 '21 06:12 synodriver

sendfile 扩展我找时间研究下。不知你有无这个扩展的使用经验? test 套件的 warning 是啥情况?能详细描述下么?

rexzhang avatar Dec 14 '21 07:12 rexzhang

sendfile 扩展我找时间研究下。不知你有无这个扩展的使用经验? test 套件的 warning 是啥情况?能详细描述下么?

我fork了上游的hypercorn服务器,加入了全部的asgi extension支持,在这 它还支持发送trailerheader,可以当grpc服务器使唤,支持http1~3,由于上游一直不merge,所以我自己发版了

pip install nonecorn

当然,他的命名空间还是hypercorn,目前我将他和gunicorn配合运行了几个月,没发现问题

test 套件的 warning 就是一些兼容性问题,应该是可以修复的

synodriver avatar Dec 14 '21 07:12 synodriver

对于一个最简单的使用这个扩展的例子,可以看看baize

synodriver avatar Dec 14 '21 07:12 synodriver

test 套件的 warning 就是一些兼容性问题,应该是可以修复的

比如

cond_put_with_not..... pass
18. cond_put_corrupt_token WARNING: PUT failed with 412 not 423
    ...................... pass (with 1 warning)

synodriver avatar Dec 14 '21 07:12 synodriver

hypercorn 以前尝试过, 当时依赖清单长度惊人,所以很长时间都没关注。刚刚看了下,似乎有所改善。但是对这个库印象不太好。没用过 sendfile 扩展,需要学习下。

rexzhang avatar Dec 14 '21 07:12 rexzhang

test 套件的 warning 就是一些兼容性问题,应该是可以修复的

比如

cond_put_with_not..... pass
18. cond_put_corrupt_token WARNING: PUT failed with 412 not 423
    ...................... pass (with 1 warning)

这个问题不只是返回状态码的问题,如果要完整实现 RFC 里面的要求,工作量不小,所以这两个告警就一直没解决。有闲心的时候再弄了,毕竟作为 WebDAV 协议实现的标杆 Apache mod_dav 也有这两个告警 ;-p

rexzhang avatar Dec 14 '21 07:12 rexzhang

hypercorn 以前尝试过, 当时依赖清单长度惊人,所以很长时间都没关注。刚刚看了下,似乎有所改善。但是对这个库印象不太好。没用过 sendfile 扩展,需要学习下。

hypercorn是所有asgi服务器里面实现最完整的,别的实现目前感觉都缺了东西,有的不支持http/2,理由还一套一套的,有的自称asgi参考服务器,结果几年了,lifespan连影子都没有,所以我就把未来的希望交给了hypercorn,给他加了更多的功能

synodriver avatar Dec 14 '21 07:12 synodriver

晚上我先来解决zerocopysend的问题吧,毕竟几个asgi服务器的源码都读过了(:

synodriver avatar Dec 14 '21 07:12 synodriver

晚上我先来解决zerocopysend的问题吧,毕竟几个asgi服务器的源码都读过了(:

请务必支持 fallback,确保不支持这个特性的环境能用

rexzhang avatar Dec 14 '21 07:12 rexzhang

请务必支持 fallback,确保不支持这个特性的环境能用

这个改动的是底层,http.response.body替换成http.response.zerocopysend,加个判断看看服务器支持与否,不会动上层逻辑,然后是aiofiles打开文件改成os.open,这样才是返回一个int的文件描述符

synodriver avatar Dec 14 '21 07:12 synodriver

现在发现一点,使用sendfile直接操作文件描述符的时候显然没法做Gzip压缩之类的操作,可能要做些取舍了

synodriver avatar Dec 14 '21 16:12 synodriver

当内容长度低于一个值,当前是 1000bytes,就不会启用Gzip。同时,如果是视频文件之类的已经压缩过的文件,也没有必要启用Gzip

rexzhang avatar Dec 15 '21 09:12 rexzhang

目前是如果asgi服务器的sendfile可用的话,那么直接跳过Gzip,直接调用zerocopysend发出去。如果不想要sendfile特性的话,可以用一个asgi中间件取消

class UseSendFileMiddlware:
    def __init__(self, app, open=True):
        self.app = app
        self.open = open

    async def __call__(self, scope,receive,send):
        if not self.open:
            if "extensions" in scope and scope["extensions"].get("http.response.zerocopysend",None):
                scope["extensions"].pop("http.response.zerocopysend")
        await self.app(scope,receive,send)

synodriver avatar Dec 15 '21 16:12 synodriver

gzip 在线压缩还是要支持的

rexzhang avatar Dec 16 '21 05:12 rexzhang

gzip 在线压缩还是要支持的

是可以支持的,不过在启用sendfile的情况下无法支持,因为要gzip的话势必读进内存,此时sendfile就没意义了,所以加了个可选的sendfile开关

synodriver avatar Dec 16 '21 08:12 synodriver

gzip 在线压缩还是要支持的

是可以支持的,不过在启用sendfile的情况下无法支持,因为要gzip的话势必读进内存,此时sendfile就没意义了,所以加了个可选的sendfile开关

应该让系统自动去切换;而不是在启动服务的时候定死,这样代价太大,感觉不值。因为磁盘 IO 性能问题,zerocopysend 很多时候发挥不出优势,但是 onfly 压缩对网络环境的适应/优化确是实实在在的

rexzhang avatar Dec 16 '21 08:12 rexzhang

应该让系统自动去切换;而不是在启动服务的时候定死,这样代价太大,感觉不值。因为磁盘 IO 性能问题,zerocopysend 很多时候发挥不出优势,但是 onfly 压缩对网络环境的适应/优化确是实实在在的

那改成对小文件启用zerocopysend?大文件走Gzip,小于1000的走zerocopysend?

synodriver avatar Dec 16 '21 08:12 synodriver

要不先放一下,我找时间调整一下代码,让 DAVResponse 处可以知道是否有必要 onfly 压缩,这样就能分别处理

rexzhang avatar Dec 16 '21 08:12 rexzhang

要不先放一下,我找时间调整一下代码,让 DAVResponse 处可以知道是否有必要 onfly 压缩,这样就能分别处理

ok,我这逻辑改起来也很方便

synodriver avatar Dec 21 '21 15:12 synodriver

5538717943fd2d593054d7c8db2002a9238ce734 添加了一个新的成员 DAVResponse.compression_method . 由用户基于 content_type 控制响应内容是否压缩,同时添加了这个成员保存压缩的类型. DAVCompressionMethod.NONE 表示未压缩

rexzhang avatar Jun 16 '22 07:06 rexzhang

未压缩的响应都可以走 zerocopysend, 剩下的问题就是如何告知 ASGI 实现这个相应是否被压缩过了

rexzhang avatar Jun 16 '22 07:06 rexzhang

可以写一个中间件来判断,等下我看看

synodriver avatar Jun 16 '22 07:06 synodriver

发现个问题,这种typing是cpython3.10才有的新功能,这样写的话3.10以下的都没法用,会出现TypeError: unsupported operand type(s) for |: 'type' and 'NoneType',可以改成Union和从typing模块import的老样式。这次不兼容是在这次提交引入的

synodriver avatar Jun 17 '22 01:06 synodriver

要改的话,一些asgi相关的typing可以从asgiref里面找,不过这应该是另一个pr了(

synodriver avatar Jun 17 '22 02:06 synodriver