上图,比较了当下比较流行的编程语言如
Rust
, Go
, JavaScript (NodeJS)
, TypeScript (Deno)
, 和 Java
等,对于高并发请求的处理速度,分别从多线程、异步、异步多线程三个维度进行了比较,单仅从异步处理维度来看,Rust是最快的,但是没想到Java的速度比GO还要快!!!当然JS和TS的异步处理是最慢的😂······
什么是并发(concurrency)?
并发是编程中最复杂的一面,并且不同的编程语言,并发的复杂程度可能是“这也太让人困惑了”,也可能是让你惊叹“这到底什么黑魔法”
并发是一种能够在同一时间处理多个任务的能力,没有特定的顺序,也不会影响最后的输出结果。并发是一个非常广泛的术语,可以通过多线程(multi-threading)、并行(parallelism)或异步处理(asynchronous process)来实现。
对比测试
前提条件:以下测试都是基于java、node、go、rust搭建的web服务器,为了尽量客观公正,这些服务器都尽可能少的使用第三方依赖,同时测试代码都一样。
用来语言测试的各版本号如下:
- Rust:
1.58.1-Stable
- Go:
1.17.6
- Java:
OpenJDK 17.0.2
- Node.js:
17.4.0
- Deno:
1.18.1
- .NET:
6.0.100
如果一个编程语言同时支持异步和多线程,我们将测试二者,并且取其中性能最好的一个作为比较结果。一个应用程序的复杂度,取决于你所选的编程语言的特性和复杂度,但是该项测试不考虑复杂度,而是尽可能利用语言所能提供的最大化的处理并发的能力,比如有些语言会提供Promise、线程池、worker等等......
郑重声明,该项测试并不是声称这是一种准确的科学方法,也不是并发的最佳标准。在不同的使用用例或者场景中,可能会有不一样的结果,并且在实际开发中,这只会更复杂。这里测试比较的只是一些最简单最基本的用例,所以不能以偏概全。
所有的实现,具体的测试代码,都在这个仓库
测试结果
wrk测试结果
用wrk
测试8线程,500次连接,间隔30s时候的性能:
wrk -t8 -c500 -d30s http://127.0.0.1:8080
dril测试结果
用drill
测试1000次和1百万次时候的并发请求:
结果
- 用
wrk
工具测试的HTTP请求中,GO在 请求/秒、延迟、吞吐量方面完胜,但是GO比Rust占用了更多的内存和CPU。但如果比较 Go HTTP、 Rust activex-web 和 Java Undertow,令人惊讶的是 Undertow 表现得更好,而 activex-web 排在第二位。 - GO TCP与Rust和Java进行比较,在这种情况下,Java和Rust均优于GO,所以上一个HTTP请求,GO完胜有可能是因为内置的HTTP库比较优秀,所以我们有理由相信肯定会有一个更优秀的第三方HTTP库能够让Rust胜过Go。
- 换个角度来比较下资源使用情况,在所有的基准测试中,Rust用的内存和CPU是最少的,Java占用的内存最多,Node多线程用的CPU最多。
- 异步Rust比多线程Rust,来处理高并发性能更好
- 在使用
drill
工具的基准测试中,异步 Java 优于 Rust!! - Java 和 Deno失败的请求比其它更多
- 从1000增加到2000次的高并发请求中,很多语言都会有失败请求,Go HTTP 和 Rust Tokio奖金百分百会出现失败请求!但是多线程的Node失败率却是最低的,并且还具有良好的性能,但是它V8引擎使用的CPU是最高的。
- Node的性能比Deno要好
简易性
相比于盲目追求极致的性能,一个语言是否简单、易用也是很重要的。所以对于异步还是多线程的选择也很关键
异步
个人认为 Node.js 和 Deno 是异步处理并发最简单的!Go是我的第二选择,因为它也很容易使用,没有妥协的功能或性能。Rust紧随其后,但是稍微更加复杂的,因为它有更多的特点需要习惯。我认为 Java 是最后一个,因为它需要更多的模板,而且异步编程比其他编程更复杂。
多线程
对于多线程处理高并发的请求,首推Rust,因为它包含了很多功能,并且在 Rust 中执行多线程是简单和不用慌的,这是由于内存和线程安全。但是Java成熟的生态系统,使得其多线程也不难使用。GO是使用最简单的,如果你对线程控制要求不高,那么我更推荐GO而不是Java。至于Node和Deno,由于其多线程处理的灵活性不如其它,所以放在最后。
生态环境
Rust在处理并发请求方面有着最好的生态环境,其次是Java和Go,也有着成熟的生态系统。至于Node和Deno相对而言社区生态就没有那么完善了。