Monoio-Linux网络IO高性能异步运行时,字节跳动中国Rust之父

Monoio-Linux网络IO高性能异步运行时,字节跳动中国Rust之父。

大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。微信公众号【梦兽编程】即可加入梦兽编程微信交流群与我交流。

语言只是我们程序员实现途径中的工具,语言的生态丰富会让我们做起事来事半功倍。从今天开始我们开始进入Rust的生态学习。

如果将中国互联网的编程语言领域比作战国时代的版图,那么阿里巴巴在Java的领域就如同雄霸一方的秦国,占据了主导地位;而Golang则有着哔哩哔哩这样的文化重镇作为支撑,如同楚国以其文化繁荣著称;C++领域则由腾讯这样的强大势力守护,类比于齐国的军事强势。至于新兴的Rust语言,它的崛起和潜力无疑将由字节跳动来引领,正如赵国在战国后期的迅速崛起一样,字节跳动在Rust的推动上将发挥关键作用。

今天将会给大家推荐来着字节跳动的异步运行时Monoio

Monoio

Monoio 是一个开源的 Rust 运行时环境,专为异步 I/O 设计。它基于 io-uring 技术,这是一个新的 Linux 内核接口,用于提高 I/O 操作的性能。Monoio 的设计目标是提供一种简单、高效的方式来处理异步 I/O,特别是在网络编程和高性能服务器应用中。

计算机科普

在讲Monoio之前,我们需要先弥补一点计算机知识。为了做到异步并发,我们需要内核提供相关的能力,来做到在某个 IO 处于阻塞状态时能够处理其他任务。我们一般会使用epoll和io-uring,进行开发异步的能力。

epoll 是 Linux 内核中的一种 I/O 事件通知机制,通过提供一个文件描述符来监视多个文件描述符的 I/O 事件,如可读、可写和异常事件。它支持边缘触发(ET)和水平触发(LT)两种模式,可以有效地处理大量的文件描述符。

主要特点包括:

  • 事件驱动的 I/Oepoll 允许应用程序只在文件描述符准备好进行 I/O 操作时才进行操作,从而减少了不必要的系统调用和 CPU 使用。
  • 高效的内存使用epoll 不需要像 selectpoll 那样每次调用时都传递所有的文件描述符,它只需要在初始化时传递一次,之后只需要传递发生变化的文件描述符。
  • 可扩展性epoll 可以处理大量的文件描述符,而不会像 selectpoll 那样随着文件描述符数量的增加而性能下降。

io-uring 是 Linux 内核提供的一种新的高性能异步 I/O 框架,它是为了解决 epoll 和其他传统 I/O 方法的局限性而设计的。io-uring 通过提供一个更高效的方式来提交和完成 I/O 操作,从而提高了 I/O 性能。

主要特点包括:

  • 提交和完成的批处理io-uring 允许应用程序提交一批 I/O 操作,并在稍后一次性获取所有完成的结果,这样可以减少系统调用的次数。
  • 零拷贝 I/Oio-uring 支持零拷贝 I/O 操作,这意味着数据可以直接在用户空间和内核空间之间传输,而无需在两者之间进行拷贝。
  • 改进的异步编程模型io-uring 提供了一种更简单、更高效的异步编程模型,它通过一组特定的系统调用(io_uring_enter 等)来提交和完成 I/O 操作。
  • 内置的轮询和通知机制io-uring 内置了轮询和通知机制,可以更有效地处理 I/O 事件。

io-uring 的优势在于零拷贝,把数据拷贝到异步时的成本更低。

实现原理

我们先看图。

从这张图我们可以看出Monoio的实现逻辑有点类似消息队列。这个队列是有单线程维护的,不会像Tokio那样充分利用多核。Redis也是单线程尽可能减少上下文切换带来的损耗。

Monoio具体可以分为以下四个点:

  1. 异步运行时:(如 Monoio)负责调度和执行这些 Future。当一个 Future 准备好继续执行时,它会通过一个 Waker 对象来唤醒。
  2. **任务(Task):**一个任务(Task)是一个异步计算单元,它通常包含一个 Future 和一个 Waker。任务可以在任何时候被挂起(当 Future 返回 Pending 时),并在稍后某个时刻(当 Future 可以继续执行时)被唤醒。
  3. 调度器(Scheduler):调度器负责将任务放入运行队列,并在适当的时候执行它们。在 Monoio 中,由于采用了单线程模型,所有的任务都在同一个线程中执行,这意味着调度器不需要处理线程间的任务迁移。
  4. **Waker:**当一个 Future 由于等待某些资源(如 I/O 操作)而无法立即完成时,它会返回 Pending 并注册一个 WakerWaker 是一个通知机制,当资源准备好时,它可以唤醒关联的 Future,使其可以继续执行。在 Monoio 中,Waker 的实现可能会与 io-uring 的异步通知机制相结合。例如,当一个异步 I/O 操作开始时,Monoio 会将其注册到 io-uring 的实例中,并设置一个 Waker。当 I/O 操作完成时,io-uring 会通过 Waker 唤醒相应的 Future

调度过程

  • 任务提交 :异步任务(Future)通过 .await 语法提交给运行时。
  • 任务挂起 :如果任务由于等待 I/O 而无法继续执行,它会返回 Pending 并注册一个 Waker
  • I/O 操作 :Monoio 将 I/O 操作提交给 io-uring,并设置 Waker 以便在操作完成时接收通知。
  • I/O 完成 :当 I/O 操作完成时,io-uring 通过 Waker 唤醒相应的任务。
  • 任务恢复:被唤醒的任务重新进入运行队列,等待下一次调度。
  • 任务执行:调度器从运行队列中取出任务并执行,直到任务完成或再次挂起。

性能考虑

Monoio 的任务调度考虑了性能优化。由于它基于单线程模型和 io-uring 技术,Monoio 可以在单个线程内高效地处理大量的异步 I/O 操作。这种设计减少了线程间切换的开销,并允许 Monoio 利用 io-uring 的高效批处理能力来提交和完成 I/O 操作。

快速入门

由于monoio兼容了tokio的api,所以直接替换即可。

rust 复制代码
[dependencies]
monoio = "0.1"
rust 复制代码
use monoio::runtime::Runtime;

#[monoio::main]
async fn main() {
    // 异步代码
}

总结

Monoio 的任务调度是围绕 io-uring 的单线程模型和 Rust 的异步编程模型构建的。它通过优化异步任务的提交、挂起和唤醒过程,以及利用 io-uring 的高效 I/O 处理能力,实现了高性能的异步 I/O 操作。在实际应用中,这种调度策略使得 Monoio 成为处理 I/O 密集型任务的高效选择。

如果您对Rust异步编程感兴趣,欢迎关注我的公众号【梦兽编程】,一起探索Rust的奥秘。如果您觉得这篇文章对您有帮助,请分享给更多需要的朋友。您的转发是我最大的动力!

相关推荐
Victor3561 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端
缘不易1 小时前
Springboot 整合JustAuth实现gitee授权登录
spring boot·后端·gitee
Kiri霧1 小时前
Range循环和切片
前端·后端·学习·golang
WizLC1 小时前
【Java】各种IO流知识详解
java·开发语言·后端·spring·intellij idea
Victor3561 小时前
Netty(19)Netty的性能优化手段有哪些?
后端
爬山算法1 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
java·后端
白宇横流学长2 小时前
基于SpringBoot实现的冬奥会科普平台设计与实现【源码+文档】
java·spring boot·后端
Python编程学习圈2 小时前
Asciinema - 终端日志记录神器,开发者的福音
后端
bing.shao3 小时前
Golang 高并发秒杀系统踩坑
开发语言·后端·golang
壹方秘境3 小时前
一款方便Java开发者在IDEA中抓包分析调试接口的插件
后端