什么是Tokio
Tokio是一个事件驱动的运行时(官网用词runtime),用来编写rust异步代码。所谓异步代码,即和同步代码对应而言。比如同步读磁盘上的文件:应用代码调用读文件的函数,函数封装了操作系统的系统调用,通知操作系统读入文件内容,操作系统把文件内容全部读出后才回到应用代码,继续执行之后的逻辑。在操作系统读文件内容的过程中,应用程序无所事事,默默地等待。而异步读入文件内容,异步调用通知操作系统读文件后立即返回,应用程序有机会在操作系统读文件时干其他事情,当操作系统读完文件内容后会以某种形式通知应用程序读文件已经完成
Tokio为异步编程提供了这些东西:
1. 一些基本工具。比如同步原语(synchronization primitives),管道(channels),计时器,延时,intervals(不知道这是什么东西,描述一个时间间隔么?)。
2. API。网络相关的tcp,udp异步函数,异步文件操作函数,异步的进程和信号管理。
3. 调度器,用来调度tasks(类似其他语言的异步框架的绿色线程或协程的概念)。
4. I/O驱动(官网用语IO driver),使用原生操作系统的事件队列接口。比如:linux下的epoll,freeBSD下的kqueue,windows下的IOCP。
5. 高性能的计时器。
快速 Fast
Tokio使用的是Rust编程语言,当然有资格有能力做到快速。Tokio要设计时也把速度放在非常重要的位置。
零开销抽象 Zero-cost abstractions
Tokio广泛使用Future这个异步概念(类似node.js中的promise,据我理解,不同语言的异步框架中使用的future和promise是非常近似的概念)。官方文档声称tokio的future和其他语言的future实现不一样。它是独一无二的(unique)。Tokio中的future会被编译器编译成一个状态机。做异步事件的同步处理时,分配内存,及其他future的实现中有开销的地方,tokio都是零开销。(我觉得协程类的异步框架都要维护状态机用来记录栈的信息么,状态机并不unique。unique的是零开销抽象)。
零开销抽象并不意味着tikio自身没有开销,而指是不可能再用其他什么方法减少开销,开销已经减到最少。
并发 Concurrency
Tokio提供了一个多线程,work-stealing(不知用哪个中文合适)的调度器。Tokio是开箱即用的,意味着,当你使用tokio运行时的时候,你就可能充分利用电脑上所有的cpu核心。
现代计算机通过增加中央处理器的核心来增加性能。所以,能够利用好多核心对于高性能的应用来说是至关重要的。
非阻塞I/O Non-blocking I/O
tokio使用操作系统原生的多路复用技术(linux的epoll,freeBSD的kqueue,windows的IOCP),一个线程可以同时管理多个socket。这样能减少系统调用(system call),提高应用的性能。
可靠 Reliable
Tokio在设计时就竭尽所能避免应用程序因使用tokio不当产生BUG,但tokio当然不可能完全做到这点。Tokio的API设计得不易于用错。这样,在项目的最后一天,你就可以信心满满地交付了。
所有权和类型系统 Ownership and type system
Tokio籍由Rust语言特有的所有权系统和严格的类型安全,能避免很多内存安全方面的错误。它能避免绝大多数常见的内存出错:访问未初始化内存,访问释放后内存,内存重复释放(Double Free)。并且,做到这些并不需要付出运行时的额外开销。(回忆自己用C/C++写的复杂应用时追踪偶发的内存出错BUG真是无比痛苦。)
另外,严格的类型安全系统也使得难以错误使用API。比如,Tokio中的互斥锁并不需要开发者显式释放。(这一点并不稀奇,C++的RAII,Go的defer,python的with也能做到,稍微现代的语言都有类似机制。)
反向压力传递 Backpressure
Tokio自带了压力反向传递的功能,这真是真是一个让人称赞的功能,真香。所谓反向压力传递,可以这么理解:当消费者消费的速度小于生产者生产的速度时,数据会在内存中越堆越多,最终把内存撑暴。反向压消费力传递指的是,消费者的压力会反向传递给生产者,让生产者减慢生产的速度以匹配消费者的消费速度。很多其他的库并没有提供这个功能,于是应用需要自己实现一个。但要实现一个高性能的类似功能并不是一件容易的事情。(回忆起自己有段时间用C++的asio异步网络库写的服务器。我的实现是当存放任务的队列到达一个阈值时就让生产者线程sleep很短一段时间,再从网络中读取数据,生产任务,结果性能大降。我相信tokio一定优雅高效地提供了这个功能。)
tokio官方文档中表示,Tokio中的生产者天生是lazy的,它们会轮循消费者,只有当消费者充许增加数据时,生产者才生产数据。
取消 Cancellation
应用的业务代码持有一个future,它描述了异步计算的结果。如果当业务代码认为并不需要这个结果时,则可以不再持有这个future(让它的生命期结束)。这样,异步计算就会及时结束,不再执行不必要的计算。官方文档表示这主要受益于Tokio的轮循设计。
多谢了Rust的权限模型,异步执行部分能通过实现drop这个特征(trait,类似c++,c#,java的接口),及时感知到future已经被丢弃了。
轻量级 Lightweight
Tokio的伸缩性很好,且伸缩时不给应用增加额外负担。这样,tokio能在资源受限的环境下发展得不错。
没有垃圾回收 No garbage collector
tokio使用的是rust语言,所有没有垃圾回收机制,也就避免了在有垃圾回收的语言中普遍存在的“世界暂停”问题。应用会周期性的启动垃圾回收,在极限性能要求的情况下,这个问题就会暴露出来。Tokio的这个特性使得它适合中实时环境中使用。
模块化 Modular
尽管Tokio提供了非常多开箱即用的功能,并且是用模块式的方式组织的。每一个component都使用一个独立的库library。用户可以精确的设定需要使用哪些特性而只导入相应的库,其他不需要使用的库则不会被编译进最终的应用程序中。很多其他著名的rust库也使用了tokio,比如hyper和actix。