大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
文章大部分内容来自作者 Daniel Lemire 的文章《Web server ‘hello world’ benchmark : Go vs Node.js vs Nim vs Bun》,部分内容做了修改,欢迎阅读。
前言
如果有现有的应用程序同时希望允许使用 HTTP 对其进行 Web 访问,或者想构建一个小型的专用 Web 应用程序,同时不想使用实际的 Web 服务器(例如 Apache 或 IIS)。
有许多流行的框架可用于编写小型 Web 应用程序。 Go 和 JavaScript (Node.js) 是目前最受欢迎的方案。 比如,Netflix 运行在 Node.js 上, 但是 Uber 从 Node.js 迁移到 Go 以获得更好的性能。 当然,还有一些冷门的技术方案,例如 Nim。
Nim (原名Nimrod),是一种静态类型的命令式编程语言, 它能在不影响运行时效率的情况下为程序员提供强大的功能, Nim语言具有高效、有表现力、优雅等特性
深入不同 Web 服务器性能特征具有一定的挑战性。 本文主要通过一个“hello world”的示例来对比不同的 Web 服务器框架,因为更复杂的应用程序可能运行速度更慢,差距加明显。
不同框架示例
下面是一个极简的 Go 服务器代码示例:
package main
import (
"io"
"fmt"
"log"
"net/http"
)
// 引入模块
func main() {
http.HandleFunc("/simple", func(w http.ResponseWriter, r *http.Request){
io.WriteString(w, "Hello!")
})
// 调用HandleFunc方法
fmt.Printf("Starting server at port 3000\n")
// 监听3000端口号
if err := http.ListenAndServe(":3000", nil); err != nil {
log.Fatal(err)
}
}
下面是一个基础的 JavaScript 服务器示例:
// 使用fastify框架
const f = require('fastify')();
f.get('/simple', async (request) => {
return 'hello';
});
f.listen({ port: 3000 })
// 监听3000端口号
.then(() => console.log('listening on port 3000'))
.catch((err) => console.error(err));
Bun 用于开发、测试、运行和打包 JavaScript 和 TypeScript 项目。 Bun 是一款专为提高速度而设计的一体化 JavaScript 运行时和工具包,配有打包器、测试运行器和与 Node.js 兼容的包管理器。使用 Bun 构建的 Server 示例代码如下:
const server = Bun.serve({
// 调用Bun.serve方法
port: 3000,
fetch(req) {
let url = new URL(req.url);
let pname = url.pathname;
if (pname == '/simple') {
return Response('Hello');
}
return new Response('Not Found.');
},
});
如果是 Nim,可以通过下面代码来完成类似功能:
import options, asyncdispatch
import httpbeast
// 导入相关包
proc onRequest(req: Request): Future[void] =
if req.httpMethod == some(HttpGet):
case req.path.get()
of "/simple":
req.send("Hello World")
else:
req.send(Http404)
run(onRequest, initSettings(port=Port(3000)))
当然,也可以将 uWebSockets.js 与 Node 一起使用。uWebSockets 是一个轻量级的 C++ WebSocket 库,为开发人员提供了构建实时应用程序的高性能且可扩展的解决方案,它的设计速度快、内存效率高并且易于集成到现有项目中。
下面是使用uWebSockets.js的代码示例:
const uWS = require('uWebSockets.js');
// 导入相应的包
const port = 3000;
const app = uWS
.App({})
.get('/simple', (res, req) => {
res.end('Hello!');
})
.listen(port, (token) => {
if (token) {
console.log('Listening to port ' + port);
} else {
console.log('Failed to listen to port ' + port);
}
});
还可以将 C++ 与 lithium 库一起使用。本质上,Lithium 是一组易于使用的 C++ 库,用于编写高性能 HTTP 服务器。
#include
int main() {
li::http_api my_api;
my_api.get("/simple") =
[&](li::http_request& request, li::http_response& response) {
response.write("hello world.");
};
li::http_serve(my_api, 3000);
}
以上代码示例都在一个强大的、基于 IceLake 的 64 核服务器上运行。 通常,此类大型服务器的时钟速度相对较低(基本频率为 2 GHz,最高为 3.2 GHz)。 下面使用一个简单的 bombardier 命令作为基准测试的一部分:
bombardier -c 10 http://localhost:3000/simple
bombardier 是一个 HTTP(S) 基准测试工具,用Go编程语言编写,并使用优秀的 fasthttp 而不是Go的默认http库,因为它具有闪电般的快速性能。
可以将并发连接数增加到 1000 (-c 1000)。最初的测试使用 autocannon,这对于这项任务来说是一个糟糕的选择。 结果表明 Nim 在这个 demo 示例上表现很好。
测试结论
测试用的 Web 服务器几乎不做任何其他工作,所以算是一个极端情况。 同时也没有进行任何额外配置,是“开箱即用”的性能。 此外,测试用的 Web 服务器可能比 Web 开发人员在实践中使用的任何服务器都更强大。
这个结果有相当大的噪音,大多数开发人员不应该完全相信这个数字,也建议尝试自己运行基准测试。不过,我也看了一些博客文章,所有结论都认为 Go 更快:
- 在 Http Server Performance: NodeJS vs. Go 中,Sunavec 发现 Go 比 Node.js 快 34%。 而且基准更加复杂,也可能更加接近现实。
- 在服务器端 I/O 性能:Node vs. PHP vs. Java vs. Go 中,Peabody 发现 Go 的扩展性比 Node.js 和 Java 好得多,PHP 排在最后。 在压力测试中,他们发现 Java 和 Node.js 不相上下,而 Go 的速度是前者的两倍。
- 在性能基准测试:Bun vs. C# vs. Go vs. Node.js vs. Python 中,测试者发现 Bun 和 Go 是赢家,而 Node.js 的运行速度约为一半。 C# 提供中等性能,而 Python 垫底。
然而,将 C、Rust 和 Zig 添加到这个基准测试中可能会更有趣。
关于 C++的解决方案最初遇到了很多困难。 事实证明,使用 Lithium 很简单:最难的部分是确保系统上安装了 OpenSSL 和 Boost。
Lithium 的作者有一个很好的方案,他解释了如何使用带有脚本的 docker 容器来运行 Lithium 服务器。 以这种方式进行意味着不必担心在系统上安装库。 在 Docker 容器中运行服务器是完全合理的,但存在性能开销,因此在基准测试中没有使用此解决方案。
参考资料
https://blog.boot.dev/golang/node-js-vs-go/
https://lemire.me/blog/2023/10/07/web-server-hello-world-benchmark-go-vs-node-js-vs-nim-vs-bun/
https://matt-42.github.io/lithium/
https://medium.com/deno-the-complete-reference/node-js-vs-deno-vs-bun-vs-go-a-re-look-at-the-hello-world-performance-d90b76ad61a5
https://betterprogramming.pub/http-server-performance-nodejs-vs-go-397751e8d275
https://www.toptal.com/back-end/server-side-io-performance-node-php-java-go
https://www.wwt.com/blog/performance-benchmarking-bun-vs-c-vs-go-vs-nodejs-vs-python