新项目再轮 asyncio 实现: kLoop - V2EX
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
fantix
V2EX    Python

新项目再轮 asyncio 实现: kLoop

  •  
  •   fantix
    fantix 2022-06-11 06:38:49 +08:00 3947 次点击
    这是一个创建于 1225 天前的主题,其中的信息可能已经有所发展或是发生改变。

    楼上读完《 Python 工匠》的同学们需要及时实践,欢迎来新项目一起锻炼!

    kLoop 是 asyncio 的(又)一个实现,用 io_uring 做主循环,搭配 kTLS 实现高效 SSL 连接。

    开发上手介绍: https://juejin.cn/post/7104795466809753607

    项目地址: https://gitee.com/fantix/kloop

    开这个项目最初是因为老板嫌 uvloop 里的 SSL 实现太慢,让我看看能不能把 Linux 的 kTLS 用起来。拖了小半年了吧,主要不是工作任务。最近忽然上瘾了,看了看好像确实可以实现,如果再加上 io_uring 做主循环,理论上应该能比 uvloop 快不少。但是老板说这样的话,产品上之后可能还是要“用 Rust 重写”,那 kLoop 我就自己搞着玩儿吧。

    写这篇介绍的文章呢,主要是想换一种挖新坑的思路。之前是自己先咔咔写,然后再补文档做网站什么的,基本上就是一两个人主要开发。这次先把开发指南搞了出来,看看能不能出现大家一起写代码的场面。

    项目主要使用 Cython 编写,DNS 解析部分混入了 Rust 。整个基础结构已经都有了,接下来的任务分工也相对比较清楚,几个大块都可以并行开发。开发环境有个 Ubuntu 22.04 就行,详见介绍文章。

    欢迎讨论!记得帮我点星星。

    18 条回复    2022-06-15 17:22:56 +08:00
    Cyanhall
        1
    Cyanhall  
       2022-06-11 09:16:12 +08:00
    原来是 EdgeDB 的大佬,顶一下
    DonaidTrump
        2
    DonaidTrump  
       2022-06-11 09:32:14 +08:00
    厉害
    ClericPy
        3
    ClericPy  
       2022-06-11 09:51:44 +08:00
    原来是 EdgeDB 的大佬... 和 1st1 一个组织的, 厉害
    lixile
        4
    lixile  
       2022-06-11 10:13:06 +08:00
    问个题外话
    架构图是用什么画的
    fantix
        5
    fantix  
    OP
       2022-06-11 10:15:48 +08:00 via iPhone
    @lixile draw.io ,好像现在叫 diagrams.net ,可以脱机当本地应用使
    mywaiting
        6
    mywaiting  
       2022-06-11 10:49:24 +08:00
    不是很明白,但是觉得很厉害~
    mayli
        7
    mayli  
       2022-06-11 17:26:54 +08:00   1
    之前我也用 io_uring + python 的 binding 自制了一些 tcp server 逻辑,体验上感觉并没有特别提升,反而是编程难度增加了不少。
    使用的库是 https://pypi.org/project/liburing/ 基本上覆盖了所有常见的操作,但是直接用 io_uring 实现业务逻辑的确是有点费劲,尤其是如果要是深度利用队列,就需要自己手动维护队列,而且要 link 起来,但是像是从 disk 读写 tcp 这种操作,最高效的是可以完全给内核 link 多个 读 / 写,但是实际的情况是 tcp 会写满被 cancel ,然后又需要 userspace 手动重试。
    haoliang
        8
    haoliang  
       2022-06-11 19:59:28 +08:00   1
    真的是拜读,这篇文章让我对 io_uring 、asyncio 了解不少(之前一直用 trio )。用词比较贴近口语,读着很畅快;详略得当,有“庖丁解牛”的即视感
    beordle
        9
    beordle  
       2022-06-11 20:32:44 +08:00 via iPhone
    感觉架构图画的好好
    joApioVVx4M4X6Rf
        10
    joApioVVx4M4X6Rf  
       2022-06-12 01:06:36 +08:00
    太牛了,希望大佬们多产出好东西
    14
        11
    14  
       2022-06-12 01:18:14 +08:00
    之前一直关注楼主 https://github.com/MagicStack/asyncpg/pull/295 这个 PR 好几年了,也在公司项目使用 asyncio/asyncpg 好多年了,期待楼主和 MagicStack 带来更多惊喜!
    fantix
        12
    fantix  
    OP
       2022-06-12 03:38:31 +08:00
    @mayli 感谢分享!我赞同您提到的三点:1 、直接使用 io_uring 或者甚至是包装过的 liburing 来做业务逻辑,编程体验并不直观; 2 、深度利用队列需要一定技巧,比如可能需要在 SQ 提交之前修改顺序,才能将需要链接的元素排在一起,因为目前链接功能只能把挨在一起的几个元素连起来; 3 、io_uring 并不提供流量控制功能,需要调用者自己搞。

    需要为其他读到这里的同学澄清的是,这些在 kLoop 中属于实现细节,参与开发的同学确实需要面对底层接口的复杂性,以及处理好各种接口异常,比如用发送队列做重试以提供高阶流量控制等等;然而,对于 kLoop 使用者而言,这些复杂性应该是封装在统一的 asyncio 接口之下的,比如用户只需调用 `loop.sendfile()`,而不需要考虑这个操作实际上可能链接了多个 io_uring 任务,包括创建管道、两次 splice 来连接文件和网络,以及流量控制所需要的重试(还没实现,大致猜测),以最高效的方式利用 io_uring 来完成任务。
    fantix
        13
    fantix  
    OP
       2022-06-12 03:53:56 +08:00
    @haoliang 感谢夸奖! trio 是好东西,异常扔得特别干净

    @14 嗯惭愧……那个 PR 确实拖了好几年才做完……
    mayli
        14
    mayli  
       2022-06-13 05:01:13 +08:00 via Android
    @fantix 感谢回复!现在业务上的需求是随机读取文件,并且附加一个 header 发送出去。
    理想的做法是使用
    os.sendfile(out_fd, in_fd, offset, count, headers=(), trailers=(), flags=0)
    这个实现的 Headers 参数, 但是这个系统调用仅在 bsd 上有实现,所以在使用 io_uring 模拟这个行为的时候,只能 link 读文件, 提交,等待读取完毕,link header/内容,发送出去。这样读取和发送都不需要经过用户态处理,但是实现相对比较冗长。
    请问在 Asyncio 框架下,能否有更高效的实现?
    我感觉目前的抽象程度,无法合理的利用更深的队列功能,比如批量提交和批量完成。这样就退化到了每次 io 也要至少触发两次系统调用,感觉性能比较低。
    fantix
        15
    fantix  
    OP
       2022-06-13 07:00:26 +08:00 via iPhone
    @mayli 好像可以创建一对 pipe ,先把 header 写进去,然后提交两个 splice(2) 任务(无须 link ?),一个连文件另一个连 socket 。这个不是 asyncio 层面的,但类似于计划中 kLoop 的 `loop.sendfile()` 实现,我还没试过,希望能帮上忙。
    mayli
        16
    mayli  
       2022-06-14 06:19:05 +08:00
    @fantix 是的,差不多是 for each(write(header), sendfile), 然后 sendfile 用 splice 实现。
    但是在实际场景中,由于每个文件块比较小,这样造成了大量的 async 调用和 syscall ,然后性能也比 read/join/send 快,可能是这样虽然节约了内存拷贝,但是 syscall 数目变多了,开销也会大。
    所以我觉得可能 io_uring 的批量操作可以优化,不过使用底层直接写很费劲,同时使用抽象化的库又失去了批量提交的性能。
    对于更深的队列这个事情,我觉得如果真的合理利用,对于 asyncio 可能是杀手级应用,至少对于 http webframework 跑分来说,都是很小的请求,syscall 可以减小一个数量级。
    fantix
        17
    fantix  
    OP
       2022-06-15 06:40:12 +08:00   1
    @mayli 我大概明白了,正如你说的,我也觉得应该是可以优化的[1],或者将来可以[2],就是可能写起来比较费劲。另外,作为抽象化的库,kLoop 我认为可以从两方面做批量优化:1 、单次主循环内批量操作:当 I/O 请求比较密集的时候,一次循环有机会批量提交处理几十上百个请求,每个请求可能都是由多个 SQE 链接实现,这样一次循环就能省几百 syscall ; 2 、提高抽象层级,直接做 HTTP 静态文件服务器[狗头]。以上还都是臆想,有待实现验证测评。

    [1]: https://lwn.net/Articles/863071/
    [2]: https://lwn.net/Articles/847951/
    zepc007
        18
    zepc007  
       2022-06-15 17:22:56 +08:00
    参拜大佬新作品
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2525 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 11:03 PVG 19:03 LAX 04:03 JFK 07:03
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86