V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
ihciah
V2EX  ›  分享创造

Monoio: 字节跳动开源 Rust Runtime

  •  8
     
  •   ihciah ·
    ihciah · 2021-12-09 09:30:06 +08:00 · 9670 次点击
    这是一个创建于 1080 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Monoio 是字节跳动服务框架组开源的基于 io-uring 的 thread-per-core 模型高性能 Rust Runtime ,旨在为高性能网络中间件等场景提供必要的运行时。

    项目仓库: https://github.com/bytedance/monoio

    背景

    过去,高性能网络中间件或服务器往往使用 C/C++ 编写,比如我们常见的 Envoy 和 Nginx 。它们往往以非常直接的方式和操作系统交互,并且得益于没有垃圾收集机制,相比有 GC 的语言(如 Golang 和 Java ),额外开销十分低,延迟稳定。

    但是开发这类组件对开发者的专业水平有较高的要求,编程范式上对开发者心智负担巨大,稍有不慎就会造成非预期的后果。举例来说,在 C++ 中要完成一次异步的网络请求,需要将整个流程按照异步点拆分成独立的纯同步函数,并以 callback 的形式将其串联——这一来大大降低了其可读性,二来状态转换和管理容易出错;并且对变量生命周期需要精细管理,否则就会出现悬垂指针等内存问题。

    为什么不试试神奇的 Rust 呢? Rust 语言通过引入所有权模型,在不引入垃圾回收的情况下保证了内存安全;并且通过语言内置的异步抽象,支持了 async + await 的异步编程模式。使用一个优秀的 Runtime ,即可像写 Golang 一样流畅地在 Rust 中平铺直叙地写异步代码——而性能并不输于 C++。

    Rust Runtime 与 thread-per-core 模型

    与 Golang 不同,Rust 语言中标准库并没有提供异步运行时(Runtime),只提供了必要的结构抽象。Runtime 负责与操作系统打交道,并对齐标准库的 Future 和 Waker 等定义,用户可以自主选择 Runtime 。

    当前被广泛使用的 Runtime 是 Tokio ,它提供了类似 Golang 调度器的实现,用户的 Task 可以在多个线程之间被调度,较为有效地利用了多核心的性能。

    但问题也随之而来:在部分强依赖高性能低延迟的场景下,调度带来的开销反而是用户不希望看到的。在核心数较多的情况下,调度开销反而会抵消调度带来的好处。

    Nginx 和 Envoy 这类组件往往使用 thread-per-core 模型,即多少核心就运行多少线程,一个任务一旦被一个线程所接收,它后续的处理则都在该线程上。这种做法下几乎没有跨线程的开销,提升了 CPU 利用率,可以较好地保持系统的线性扩展性。此外,由于没有跨线程,处理逻辑也可以尽可能地利用 thread local 的便利,多数时候无需加锁便可操作共享数据。

    面向这类场景,Monoio 基于 io-uring 致力于提供最佳的性能;另外,我们还定义了一个更适合 io-uring 的 IO trait 。

    性能

    monoio-bench-100B.png

    monoio-bench-1C-250conn-qps.png

    我们对比了 Monoio 、Tokio 和 Glommio (另一个类似的 Runtime ,但在性能目标上不如 Monoio 激进)。

    在绝大多数测试中,Monoio 都具有更低的延迟和更高的吞吐。对比 Tokio ,在多核场景下 Monoio 可以提供 2 到 3 倍的性能提升(原因主要在于模型上,没有了跨线程同步开销);而对比 Glommio ,我们可以在降低延迟的同时,节省约 1/4 到 1/3 的 CPU 占用(性能提升在于更优的调度实现,io-uring 批量 submit )。

    更进一步的测试报告和设计上的权衡在 Github Repo 中有详细的文档。

    另外,我们还对比了生产中使用的(epoll based) Nginx ,在 Proxy 场景下基于 Monoio 写的 TCP 代理可以获得差不多的性能(连接数较多时 Monoio 性能优于 Nginx ,较少时差于 Nginx ,数据整体上差不多)。与 Envoy 的 TCP Proxy 对比也表明 Monoio 有非常明显的性能优势。

    结语

    Monoio 提供了 thread-per-core 场景下最高性能的 Runtime 实现。我们的目标是能够让 Rust 在高性能场景下成为替换 C/C++ 的更好选择。目前字节已经开始基于 Rust 和 Monoio 构建下一代 Service Mesh 。

    当然,没有什么 Runtime 是绝对最佳的选择,Runtime 的选型还是要根据具体的业务场景来。希望我们的 Monoio 可以给某些场景用户多一种选择。

    在 Monoio 的设计和实现中我们大量参考了 Tokio 等同类产品,感谢这些项目的贡献者;也希望 Monoio 能够在大家的共同努力下变得更加完善更加易用。

    参考资料

    另外,在开发过程中我也总结了一些东西,写成了几篇博客,感兴趣可以看这里:Rust Runtime 设计与实现

    49 条回复    2022-08-23 13:41:38 +08:00
    Mithril
        1
    Mithril  
       2021-12-09 09:33:12 +08:00   ❤️ 9
    BS:你们为了不学 C++也真是费尽心机了
    fyooo
        2
    fyooo  
       2021-12-09 09:40:47 +08:00   ❤️ 1
    谢谢分享,字节很多开源项目都充满干货啊
    ihciah
        3
    ihciah  
    OP
       2021-12-09 09:48:09 +08:00
    @Mithril 用 Rust 上线更安心~

    @fyooo 谢谢支持,也欢迎大家参与!
    PureWhiteWu
        4
    PureWhiteWu  
       2021-12-09 09:48:17 +08:00
    Monoio 之父 YYDS
    Kilerd
        5
    Kilerd  
       2021-12-09 09:55:01 +08:00
    thread-per-core 的一个问题在于没有 work-steal ,那么就很有可能存在一核干活,N 核围观的情况。这十分考验任务进哪个 thread 的调度。

    tokio 作为一个通用的 async runtime 必然是要设计成 work-steal 的。

    但是一个问题是,你们有没有试过你们的产品和 自己启动 thread ( thread 里面各自启动一个 tokio 的 single thread 的 rt ) 的性能比较呢? 这样的测试可能才是对等的。

    另外,Rust 现阶段跟 io-uring 并不是很搭,不知道你们是怎么解决 buffer 的安全性问题的。
    kernelerror
        6
    kernelerror  
       2021-12-09 09:55:48 +08:00 via Android
    支持👍
    RtfscRtfm
        7
    RtfscRtfm  
       2021-12-09 09:57:44 +08:00
    Monoio 之父 YYDS
    ihciah
        8
    ihciah  
    OP
       2021-12-09 10:00:52 +08:00
    @Kilerd 是的,我们的场景上和 Tokio 是不同的。
    可以看和 tokio 单线程的对比数据(这时其实主要差别就在于 epoll 和 io-uring 了)。
    buffer 我们采用的 Tokio-uring 的做法,直接拿所有权,用完还回去。
    CatCode
        9
    CatCode  
       2021-12-09 10:32:33 +08:00   ❤️ 1
    感谢开源
    Croxx
        10
    Croxx  
       2021-12-09 11:06:34 +08:00 via iPhone
    在 github 看到之后就在找作者的 twitter ,没想到在 v 站看到了😂
    coeru
        11
    coeru  
       2021-12-09 11:39:17 +08:00
    成功
    abcbuzhiming
        12
    abcbuzhiming  
       2021-12-09 11:52:02 +08:00   ❤️ 1
    @Mithril 我看了 rust 之后觉得 rust 的学习曲线没有比 c++低到哪里去,可能唯一的优势就是没有 C++那么多的特性
    libook
        13
    libook  
       2021-12-09 11:57:59 +08:00
    最近刚看完 Rust 的教程 ,不知道以后能不能用 Rust 写微服务。
    PureWhiteWu
        14
    PureWhiteWu  
       2021-12-09 11:59:46 +08:00
    @libook 当然能用,可以期待一下我们即将开源的微服务框架~
    tulongtou
        15
    tulongtou  
       2021-12-09 12:53:25 +08:00 via iPhone
    支持。话说这是孵化的产品,还是已经再生产环境使用了?
    mywaiting
        16
    mywaiting  
       2021-12-09 12:58:27 +08:00   ❤️ 2
    虽然看不懂,但是看到 Rust 写的我就想点赞
    ihciah
        17
    ihciah  
    OP
       2021-12-09 13:17:12 +08:00
    @tulongtou 目前公司内部计划基于这套 Runtime 做下一代高性能 MeshProxy ,但尚无生产实际使用。开源出来也是希望能够和大家一起建设生态:)
    CSM
        18
    CSM  
       2021-12-09 13:25:43 +08:00   ❤️ 2
    提个建议,叫 Rust async runtime ,只看 Rust runtime 容易摸不着头脑
    araaaa
        19
    araaaa  
       2021-12-09 13:29:07 +08:00 via iPhone
    啥时候支持多平台
    tulongtou
        20
    tulongtou  
       2021-12-09 14:01:20 +08:00   ❤️ 1
    @ihciah 挺好的,希望项目能长期发现下去。
    feather12315
        21
    feather12315  
       2021-12-09 14:04:31 +08:00 via Android
    @araaaa #19 不可能的,io uring 是 Linux 独有的
    nameyukan
        22
    nameyukan  
       2021-12-09 14:06:41 +08:00
    看了下代码,咋都没测试的,质量怎么保证?
    ScepterZ
        23
    ScepterZ  
       2021-12-09 14:07:47 +08:00
    不懂就问,压测曲线上,蓝色的在 40000 的时候 cpu 就 100%了,然后在此基础上 150%的 qps ,到 60000 的时候,延迟基本没变化,这合理吗
    codehz
        24
    codehz  
       2021-12-09 14:13:18 +08:00 via Android
    tinkerer
        25
    tinkerer  
       2021-12-09 14:48:06 +08:00   ❤️ 1
    谢谢你们的贡献
    ihciah
        26
    ihciah  
    OP
       2021-12-09 16:10:28 +08:00
    @ScepterZ 因为连接数是固定的,所以在达不到设定的 QPS limit (限制做在客户端)时,在服务端的感知是完全一样的。

    @nameyukan 感谢批评,测试这块后续逐渐会逐渐补齐。目前 unsafe 不太多,通过 Rust 本身基本可以保证没有内存问题。逻辑问题确实需要,这块目前在快速推进还不完善。

    @araaaa windows 暂时不会支持,后续可能会通过引入 mio 来支持 mac 和无 iouring 的 linux 。
    Kilerd
        27
    Kilerd  
       2021-12-09 16:50:15 +08:00
    @ihciah tokio-uring 我印象中他为了保证所有权的安全,其实是把数据从内核态拷贝了一份到用户态,本质上并没有「真正完全」使用 io-uring 的优势(后续有没有更改我没有太了解了)

    请问你们是如何解决这个问题的?
    ihciah
        28
    ihciah  
    OP
       2021-12-09 17:20:55 +08:00
    @Kilerd 没有拷贝,我们这部分设计(以及部分代码)其实是直接从 tokio-uring 里搬的,不过我们基于 GAT 把这部分抽成了 trait 。
    带所有权的接口设计就是用户扔所有权进来,io 完成时再扔回去。这样可以保证在 sq 推进去之后 sq 中的 buffer 指针一直是有效的。
    Kilerd
        29
    Kilerd  
       2021-12-09 17:32:56 +08:00
    @ihciah 多谢解答,谢谢。
    Mistwave
        30
    Mistwave  
       2021-12-09 17:53:48 +08:00 via iPhone
    赞 字节内部 rust 用的广泛吗?
    kerro1990
        31
    kerro1990  
       2021-12-09 20:10:37 +08:00
    @Mistwave 一看就是 KPI 项目,字节内部都不用
    kerro1990
        32
    kerro1990  
       2021-12-09 20:13:34 +08:00
    @Kilerd KPI 项目无疑了,字节内部大部分都是用 go 和 py
    ihciah
        33
    ihciah  
    OP
       2021-12-09 20:46:13 +08:00 via iPhone
    @Mistwave

    并不广泛,在业务侧用户很少。我们认为,一来是因为业务不那么 care 性能,而 rust 上手成本又很高,二来是因为想用也用不了,因为 rpc 等组件没有。我们前阵子在做这堆组件(我之前有帖子分享做 rpc 框架),给想用却没法用的业务用的机会。现在业务基本能用 rust 写了,不过使用者仍不算多。

    但是我们认为在基础架构内部 rust 的未来还是很美好的。内核组已经有一些应用了,我们 Mesh 这边也计划造下一代基于 rust 的数据面代理,公司内的 rust 群也有接近 2000 人。

    @kerro1990

    你不用不代表没人用哦。
    Kilerd
        34
    Kilerd  
       2021-12-09 21:20:27 +08:00 via iPhone
    @kerro1990 可能是,也可能不是。 字节内部还是有不少团队在用 rust 的,但是业务团队应该很少就是了。

    开源本意很好,但是还是要看内部发展和开源部分会不会分叉。 是不是 kpi 开源还要看之后的维护,目前看起来也只够字节内部部分团队使用而已,而且不太兼容市面上 tokio 和 async-std 的所有生态,生态很重要,async-std 追赶了一年多还影响不到 tokio 的地位,这个作品就更加不可能了。
    Suclogger
        35
    Suclogger  
       2021-12-09 21:57:13 +08:00   ❤️ 1
    rust 生态又添一个巨佬
    linshenqi
        36
    linshenqi  
       2021-12-09 22:33:45 +08:00   ❤️ 1
    支持下~很久以前就想学 rust ,但一直用 golang 就没在搞了。。
    ihciah
        37
    ihciah  
    OP
       2021-12-09 23:16:57 +08:00
    @Kilerd 是的,我们开源的本意也是为了一起建设这类基于完成通知 IO 的生态。这个 rt 只面向限定场景,并且至少我们内部确实需要用,所以后续也会持续投入。
    statumer
        38
    statumer  
       2021-12-10 10:47:41 +08:00 via iPhone   ❤️ 1
    太牛啦,一直在找 rust 用 io uring 一个比较好的方案
    nameyukan
        39
    nameyukan  
       2021-12-10 11:41:50 +08:00
    @ihciah 在服务和质量没有达到一定程度,现在开源出来是否过早,刚看了一眼也缺少一些案例和文档。从为开发者负责的角度来讲和后续自身维护的角度,我都觉得太着急了一点。
    ihciah
        40
    ihciah  
    OP
       2021-12-10 13:12:38 +08:00 via iPhone
    @nameyukan

    谢谢批评,目前文档确实欠缺。当前也并不是生产 ready 的,我们明确说了项目仍处于非常早期的阶段,相信你从 0.0.x 的版本号上也能看出来。
    但我觉得,开源并不是一定要先做到完美。众人拾柴火焰高,我们希望能够和志同道合的人、有同样需求的人一起来把它变完美。作为一个不涉及公司业务细节的通用组件,开源开发并不会带来任何不便;反而在大家的监督和 review 下 commit 质量会更好。
    neon0
        41
    neon0  
       2021-12-10 13:55:37 +08:00
    @kernelerror 你的头像是哪方卧底
    whimsySun
        42
    whimsySun  
       2021-12-10 15:46:25 +08:00
    挺感兴趣的,monoio 是不是也适合用来做边缘网关,看压测说大连接的情况下性能好雨 envoy
    ihciah
        43
    ihciah  
    OP
       2021-12-10 20:15:52 +08:00
    @whimsySun 这个确实是我们的目标场景(我们组主要做 ServiceMesh )。Edge Proxy 和 Mesh Proxy 都是比较理想的应用场景。
    whimsySun
        44
    whimsySun  
       2021-12-10 21:03:54 +08:00 via iPhone
    @ihciah 能不能加下 tg ,我们有在弄 edge proxy ,也有在接 istio
    ihciah
        45
    ihciah  
    OP
       2021-12-10 21:54:51 +08:00 via iPhone
    @whimsySun 场景是合适,我们内部也决定做,但是还没做出来🤣 (tg 同名
    kkocdko
        46
    kkocdko  
       2021-12-12 11:21:20 +08:00 via Android
    太强啦~
    昨天才在 rustcc 那边看到,没想到作者也在 V 站
    pupie
        47
    pupie  
       2021-12-13 10:52:19 +08:00
    感谢开源~
    orafy
        48
    orafy  
       2022-02-14 09:50:28 +08:00
    Service Mesh 有一个优化方向可以参考 Cilium ,更多的利用 XDP 。
    兼容性比 DPDK 的转发层好。

    Kernel bypass 的技术能 10~50 倍于 io-uring/epoll
    novolunt
        49
    novolunt  
       2022-08-23 13:41:38 +08:00
    @libook
    @PureWhiteWu
    可以使用 ebpf 做 service mesh ,cilium 的 mesh 方案
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2836 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 07:23 · PVG 15:23 · LAX 23:23 · JFK 02:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.