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++。
与 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 、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 设计与实现。
1
Mithril 2021-12-09 09:33:12 +08:00 9
BS:你们为了不学 C++也真是费尽心机了
|
2
fyooo 2021-12-09 09:40:47 +08:00 1
谢谢分享,字节很多开源项目都充满干货啊
|
4
PureWhiteWu 2021-12-09 09:48:17 +08:00
Monoio 之父 YYDS
|
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 的安全性问题的。 |
6
kernelerror 2021-12-09 09:55:48 +08:00 via Android
支持👍
|
7
RtfscRtfm 2021-12-09 09:57:44 +08:00
Monoio 之父 YYDS
|
8
ihciah OP @Kilerd 是的,我们的场景上和 Tokio 是不同的。
可以看和 tokio 单线程的对比数据(这时其实主要差别就在于 epoll 和 io-uring 了)。 buffer 我们采用的 Tokio-uring 的做法,直接拿所有权,用完还回去。 |
9
CatCode 2021-12-09 10:32:33 +08:00 1
感谢开源
|
10
Croxx 2021-12-09 11:06:34 +08:00 via iPhone
在 github 看到之后就在找作者的 twitter ,没想到在 v 站看到了😂
|
11
coeru 2021-12-09 11:39:17 +08:00
成功
|
12
abcbuzhiming 2021-12-09 11:52:02 +08:00 1
@Mithril 我看了 rust 之后觉得 rust 的学习曲线没有比 c++低到哪里去,可能唯一的优势就是没有 C++那么多的特性
|
13
libook 2021-12-09 11:57:59 +08:00
最近刚看完 Rust 的教程 ,不知道以后能不能用 Rust 写微服务。
|
14
PureWhiteWu 2021-12-09 11:59:46 +08:00
@libook 当然能用,可以期待一下我们即将开源的微服务框架~
|
15
tulongtou 2021-12-09 12:53:25 +08:00 via iPhone
支持。话说这是孵化的产品,还是已经再生产环境使用了?
|
16
mywaiting 2021-12-09 12:58:27 +08:00 2
虽然看不懂,但是看到 Rust 写的我就想点赞
|
17
ihciah OP @tulongtou 目前公司内部计划基于这套 Runtime 做下一代高性能 MeshProxy ,但尚无生产实际使用。开源出来也是希望能够和大家一起建设生态:)
|
18
CSM 2021-12-09 13:25:43 +08:00 2
提个建议,叫 Rust async runtime ,只看 Rust runtime 容易摸不着头脑
|
19
araaaa 2021-12-09 13:29:07 +08:00 via iPhone
啥时候支持多平台
|
21
feather12315 2021-12-09 14:04:31 +08:00 via Android
@araaaa #19 不可能的,io uring 是 Linux 独有的
|
22
nameyukan 2021-12-09 14:06:41 +08:00
看了下代码,咋都没测试的,质量怎么保证?
|
23
ScepterZ 2021-12-09 14:07:47 +08:00
不懂就问,压测曲线上,蓝色的在 40000 的时候 cpu 就 100%了,然后在此基础上 150%的 qps ,到 60000 的时候,延迟基本没变化,这合理吗
|
24
codehz 2021-12-09 14:13:18 +08:00 via Android
|
25
tinkerer 2021-12-09 14:48:06 +08:00 1
谢谢你们的贡献
|
26
ihciah OP |
27
Kilerd 2021-12-09 16:50:15 +08:00
@ihciah tokio-uring 我印象中他为了保证所有权的安全,其实是把数据从内核态拷贝了一份到用户态,本质上并没有「真正完全」使用 io-uring 的优势(后续有没有更改我没有太了解了)
请问你们是如何解决这个问题的? |
28
ihciah OP @Kilerd 没有拷贝,我们这部分设计(以及部分代码)其实是直接从 tokio-uring 里搬的,不过我们基于 GAT 把这部分抽成了 trait 。
带所有权的接口设计就是用户扔所有权进来,io 完成时再扔回去。这样可以保证在 sq 推进去之后 sq 中的 buffer 指针一直是有效的。 |
30
Mistwave 2021-12-09 17:53:48 +08:00 via iPhone
赞 字节内部 rust 用的广泛吗?
|
33
ihciah OP |
34
Kilerd 2021-12-09 21:20:27 +08:00 via iPhone
@kerro1990 可能是,也可能不是。 字节内部还是有不少团队在用 rust 的,但是业务团队应该很少就是了。
开源本意很好,但是还是要看内部发展和开源部分会不会分叉。 是不是 kpi 开源还要看之后的维护,目前看起来也只够字节内部部分团队使用而已,而且不太兼容市面上 tokio 和 async-std 的所有生态,生态很重要,async-std 追赶了一年多还影响不到 tokio 的地位,这个作品就更加不可能了。 |
35
Suclogger 2021-12-09 21:57:13 +08:00 1
rust 生态又添一个巨佬
|
36
linshenqi 2021-12-09 22:33:45 +08:00 1
支持下~很久以前就想学 rust ,但一直用 golang 就没在搞了。。
|
37
ihciah OP @Kilerd 是的,我们开源的本意也是为了一起建设这类基于完成通知 IO 的生态。这个 rt 只面向限定场景,并且至少我们内部确实需要用,所以后续也会持续投入。
|
38
statumer 2021-12-10 10:47:41 +08:00 via iPhone 1
太牛啦,一直在找 rust 用 io uring 一个比较好的方案
|
39
nameyukan 2021-12-10 11:41:50 +08:00
@ihciah 在服务和质量没有达到一定程度,现在开源出来是否过早,刚看了一眼也缺少一些案例和文档。从为开发者负责的角度来讲和后续自身维护的角度,我都觉得太着急了一点。
|
40
ihciah OP @nameyukan
谢谢批评,目前文档确实欠缺。当前也并不是生产 ready 的,我们明确说了项目仍处于非常早期的阶段,相信你从 0.0.x 的版本号上也能看出来。 但我觉得,开源并不是一定要先做到完美。众人拾柴火焰高,我们希望能够和志同道合的人、有同样需求的人一起来把它变完美。作为一个不涉及公司业务细节的通用组件,开源开发并不会带来任何不便;反而在大家的监督和 review 下 commit 质量会更好。 |
41
neon0 2021-12-10 13:55:37 +08:00
@kernelerror 你的头像是哪方卧底
|
42
whimsySun 2021-12-10 15:46:25 +08:00
挺感兴趣的,monoio 是不是也适合用来做边缘网关,看压测说大连接的情况下性能好雨 envoy
|
43
ihciah OP @whimsySun 这个确实是我们的目标场景(我们组主要做 ServiceMesh )。Edge Proxy 和 Mesh Proxy 都是比较理想的应用场景。
|
46
kkocdko 2021-12-12 11:21:20 +08:00 via Android
太强啦~
昨天才在 rustcc 那边看到,没想到作者也在 V 站 |
47
pupie 2021-12-13 10:52:19 +08:00
感谢开源~
|
48
orafy 2022-02-14 09:50:28 +08:00
Service Mesh 有一个优化方向可以参考 Cilium ,更多的利用 XDP 。
兼容性比 DPDK 的转发层好。 Kernel bypass 的技术能 10~50 倍于 io-uring/epoll |
49
novolunt 2022-08-23 13:41:38 +08:00
|