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 · 3511 次点击
    这是一个创建于 930 天前的主题,其中的信息可能已经有所发展或是发生改变。

    楼上读完《 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 的大佬,顶一下
    tulongtou
        2
    tulongtou  
       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   ·   实用小工具   ·   943 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 21:04 · PVG 05:04 · LAX 13:04 · JFK 16:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.