Draft: support zerocopysend asgi extension
第一个原型,还会添加更多测试。
实现细节:
- 发送文件时完全使用os.open读取。这样做是因为一旦发现zerocopy不可用,还能使用os.read来读取为bytes,但是如果一开始就读取为bytes那是无论如何都不能再变回文件描述符了
迭代文件描述符的时候使用了run_in_executor,在zerocopy不可用的时候提高效率
这个中间件可以作为打开zerocopysend的开关来使用。
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 "http.response.zerocopysend" in scope["extensions"]:
scope["extensions"].pop("http.response.zerocopysend")
await self.app(scope,receive,send)
或许可以增加一个配置项?
既然要集成进别的asgi框架,那logger应该也可以考虑下。目前的logger使用的比较散乱,是否可以添加一个app级别的logger统一使用?
- zero-copy 是否在 ASGI 接口部分应该是服务启动后即可知道的.
- 应该可以一开始只是一个
这个中间件可以作为打开zerocopysend的开关来使用。
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 "http.response.zerocopysend" in scope["extensions"]: scope["extensions"].pop("http.response.zerocopysend") await self.app(scope,receive,send)或许可以增加一个配置项?
使用配置来控制,这样也能方便 CLI 等方式来控制.
class Config():
# response
compression: Compression = Compression()
cors: CORS = CORS()
enable_dir_browser: bool = True
enable_asgi_zero_copy: bool = True
代码部分检查这个新的控制变量来执行不同的行为
既然要集成进别的asgi框架,那logger应该也可以考虑下。目前的logger使用的比较散乱,是否可以添加一个app级别的logger统一使用?
这个开一个新的 Issue 讨论比较好,如果你有比较完整的想法的话
迭代文件描述符的时候使用了run_in_executor,在zerocopy不可用的时候提高效率
当 zerocopy 不可用时需要完全回落到以前的代码;也就是说 zerocopy 部分需要以可挂载的方式添加进来
迭代文件描述符的时候使用了run_in_executor,在zerocopy不可用的时候提高效率
当 zerocopy 不可用时需要完全回落到以前的代码;也就是说 zerocopy 部分需要以可挂载的方式添加进来
目前是provider统一读取为文件描述符了,要完全回到原来那种很困难。主要是要考虑多方面因素
- ASGI服务器是否支持zerocopysend?
- 用户是否配置了zerocopysend?
- 用户启用了zerocopysend但是ASGI服务器不支持怎么办?
- 目前的构架,往里面更改整个实现逻辑实在是有点困难了,一旦zerocopy不可用,还能使用os.read来读,但是bytes就不可能变成文件描述符的,基于此可以认为让
FileSystemProvider返回fd: int是比较合理的
This pull request introduces 5 alerts when merging 5257dec2e8afe7a328836e73ea471a4c5301897b into 1b0dc6be9b13692373a25b2d6c27bae9d227c7e4 - view on LGTM.com
new alerts:
- 5 for Module-level cyclic import
迭代文件描述符的时候使用了run_in_executor,在zerocopy不可用的时候提高效率
当 zerocopy 不可用时需要完全回落到以前的代码;也就是说 zerocopy 部分需要以可挂载的方式添加进来
目前是provider统一读取为文件描述符了,要完全回到原来那种很困难。主要是要考虑多方面因素
* ASGI服务器是否支持zerocopysend? * 用户是否配置了zerocopysend? * 用户启用了zerocopysend但是ASGI服务器不支持怎么办? * 目前的构架,往里面更改整个实现逻辑实在是有点困难了,一旦zerocopy不可用,还能使用os.read来读,但是bytes就不可能变成文件描述符的,基于此可以认为让`FileSystemProvider`返回`fd: int`是比较合理的
前三条都是静态信息,启动完成后即确定的.架构的话,可以先确定需要在什么地方确定什么状态来调整
ASGI服务器是否支持zerocopysend没法在服务器启动的时候知道,只有等第一个request来了以后去scope里面看
ASGI服务器是否支持zerocopysend没法在服务器启动的时候知道,只有等第一个request来了以后去scope里面看
这就有点离谱了,难道 ASGI 服务器在启动后不能确定自己是否支持 zerocopysend? 这个应该可以修改 ASGI 服务器来实现吧? 而且可以认定,当用户设置了开启 enable_asgi_zero_copy 选项,即表示用户明确的知道 ASGI 服务器支持;在这个设计下,这个选项默认关闭即可
ASGI服务器是否支持zerocopysend没法在服务器启动的时候知道,只有等第一个request来了以后去scope里面看
这就有点离谱了,难道 ASGI 服务器在启动后不能确定自己是否支持 zerocopysend? 这个应该可以修改 ASGI 服务器来实现吧? 而且可以认定,当用户设置了开启 enable_asgi_zero_copy 选项,即表示用户明确的知道 ASGI 服务器支持;在这个设计下,这个选项默认关闭即可
因为asgiapp可以部署在各种asgi兼容服务器上,uvicorn, hypercorn, daphne, nginx unit, prehttp, nonecorn都可以,甚至还可以是个云函数,环境多种多样,app甚至都不知道他在什么服务器上面跑。正规做法是去scope判定,不过如果用户明确设置了这个,则可以认为用户知道这样做会有什么后果,那可以不处理异常了?
然后是目前的实现相关。在FileSystemProvider._do_get,要判断是否使用zerocopy就得从这开始了。但是目前提供的函数是DAVResponse.can_be_compressed,它的参数是header的一个字段和rule,可是在FileSystemProvider._do_get里面header还没生成呢,要硬改的话改不好会cycle import
因为asgiapp可以部署在各种asgi兼容服务器上,uvicorn, hypercorn, daphne, nginx unit, prehttp, nonecorn都可以,甚至还可以是个云函数,环境多种多样,app甚至都不知道他在什么服务器上面跑。正规做法是去scope判定,不过如果用户明确设置了这个,则可以认为用户知道这样做会有什么后果,那可以不处理异常了?
考虑到:会做性能调整的用户应该是高级用户的定位.只要在遇到明确是配置问题的地方抛出“配置/初始化”异常,同时退出服务;应该就可以了
因为asgiapp可以部署在各种asgi兼容服务器上,uvicorn, hypercorn, daphne, nginx unit, prehttp, nonecorn都可以,甚至还可以是个云函数,环境多种多样,app甚至都不知道他在什么服务器上面跑。正规做法是去scope判定,不过如果用户明确设置了这个,则可以认为用户知道这样做会有什么后果,那可以不处理异常了?
考虑到:会做性能调整的用户应该是高级用户的定位.只要在遇到明确是配置问题的地方抛出“配置/初始化”异常,同时退出服务;应该就可以了
这个就简单了,把这个event原样扔给asgi服务器,asgi服务器收到不支持的事件自己会报错的
然后是目前的实现相关。在
FileSystemProvider._do_get,要判断是否使用zerocopy就得从这开始了。但是目前提供的函数是DAVResponse.can_be_compressed,它的参数是header的一个字段和rule,可是在FileSystemProvider._do_get里面header还没生成呢,要硬改的话改不好会cycle import
判断是否会执行压缩其实是基于 response 的 header 中的 Content-Type 确定的. 而 Content-Type 是有 request 中 GET 方法需要获取的这个文件的文件名和文件格式来确定的.
也就是说在 FileSystemProvider._do_get() 中, 如果配置中启用了 ZCS 就做一个判断,然后根据判断后的结果执行 ZCS 或者回落执行以前的代码. 挂载的方式加入是完全可行的
另外, PR 的代码请先执行 isort 再执行 black 格式化代码后再提交. 相关配置我都是做好了的,只需要在项目根目录执行即可
ASGI服务器是否支持zerocopysend没法在服务器启动的时候知道,只有等第一个request来了以后去scope里面看
其实可以去 asgiref 提一个需求. 添加一个标准特性:提供一个 API, 允许在获取 ASGI 实例的当前支持的特性和扩展清单
django组织的成员挺固执的……估计他们不会听,原来我那个trailingheader的扩展就没通过,我就自己实现了
This pull request introduces 7 alerts when merging 6d353c1a2acd2ca91d39c3bfca1cb65e379eda58 into 1b0dc6be9b13692373a25b2d6c27bae9d227c7e4 - view on LGTM.com
new alerts:
- 6 for Module-level cyclic import
- 1 for Unused import
This pull request introduces 1 alert when merging 41272e2ccd1013b8194a64bbccfdc45b20b8c20d into 1b0dc6be9b13692373a25b2d6c27bae9d227c7e4 - view on LGTM.com
new alerts:
- 1 for Unused import
This pull request introduces 1 alert when merging 28a236b0227dc7f6271d62e8bea9d60095127390 into 1b0dc6be9b13692373a25b2d6c27bae9d227c7e4 - view on LGTM.com
new alerts:
- 1 for Module is imported with 'import' and 'import from'
单元测试中的get_response_content需要修改一下,现在response._content可能是异步生成器也可能是zerocopy的fd,或者默认enable_asgi_zero_copy: bool是false?这里做了一个假设:用户会使用相应的asgi服务器来部署,不过看情况不能这样,还是应该保守一些
单元测试中的
get_response_content需要修改一下,现在response._content可能是异步生成器也可能是zerocopy的fd,或者默认enable_asgi_zero_copy: bool是false?这里做了一个假设:用户会使用相应的asgi服务器来部署,不过看情况不能这样,还是应该保守一些
需要如何改?欢迎直接提方案
单元测试部分,可以先完善 PR 涉及的部分,有冲突部分后续解决也可.并行推进效率高很多
相关的单元测试加了个patch,应该好了
Codecov Report
Attention: Patch coverage is 43.01075% with 53 lines in your changes are missing coverage. Please review.
Project coverage is 65.25%. Comparing base (
ac3161d) to head (e4b9946). Report is 5 commits behind head on main.
:exclamation: Current head e4b9946 differs from pull request most recent head df4e798. Consider uploading reports for the commit df4e798 to get more accurate results
| Files | Patch % | Lines |
|---|---|---|
| asgi_webdav/response.py | 42.50% | 23 Missing :warning: |
| asgi_webdav/helpers.py | 26.92% | 19 Missing :warning: |
| asgi_webdav/provider/file_system.py | 56.00% | 11 Missing :warning: |
Additional details and impacted files
@@ Coverage Diff @@
## main #20 +/- ##
==========================================
- Coverage 65.99% 65.25% -0.75%
==========================================
Files 24 24
Lines 2770 2849 +79
==========================================
+ Hits 1828 1859 +31
- Misses 942 990 +48
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
可否添加些性能对比测试? uvicorn/nonecorn 打开 ZeroCopy/nonecorn 关闭 ZeroCopy
我做过一般网站的性能测试,确实可以有数倍的提升
可否针对这个项目做一个如上三个环境的对比性能测试? 可以放在 performance/asgi_zero_copy 目录下
测试结果以文本文件的方式存储,可以简单的(在命令行)重定向输出到文件