之前简单测过go的性能,高并发场景下确实比node会好一些,一直想找个时间系统性地测一下,手头正好有一台前段时间买的游戏主机,装了ubuntu就开测了node
准备工做npm
测试机和试压机系统都是ubuntu 18.04.1 首先安装node和go,版本分别以下: node 10.13.0 go 1.11 测试机和试压机修改fd的限制 ulimit -n 100000 ,不然fd很快就用完了。 若是是试压机是单机,而且QPS很是高的时候,也许你会常常见到试压机有N多的链接都是TIME_WAIT状态,具体缘由能够在网上搜一下,执行如下命令便可: sysctl -w net.ipv4.tcp_tw_reuse=1 $ sysctl -w net.ipv4.tcp_tw_recycle=1 测试工具我用的是siege,版本是3.0.8。 测试的js代码和go代码分别以下: Node(官网的cluster示例代码)ubuntu
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
console.log(`Master ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer((req, res) => {
res.end('hello world ');
}).listen(8000);
console.log(`Worker ${process.pid} started`);}
Go
package main
import(
"net/http"
"fmt"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello world")
}
func main() {
http.HandleFunc("/", hello);
err := http.ListenAndServe(":8000", nil);
if err != nil {
}
}
复制代码
开始测试浏览器
首先开始并发量的测试,然而。。。游戏主机的CPU是i7-8700K,性能太强,以致于拿了两台mac也没能压满。。。bash
四处寻找一番,找到了好几年前花了千把块钱配的nas,上去一看是颗双核的i3-4170,妥了,这下确定没问题多线程
正式开始并发
跳过了小插曲,直接开测tcp
I/O密集型场景微服务
Node - 多进程模型,能够看到由于全部请求都由master进程转发,master进程成为了瓶颈,在CPU占用100%的状况下,worker进程仅仅只占了50%,所以总体CPU利用率只有70%。 qps: 6700 记一次Node和Go的性能测试 Go - 单进程多线程模型,系统剩余CPU差很少也有30%,查了一下缘由是由于网卡已经被打满了,千兆网卡看了下已经扎扎实实被打满了。 qps: 37000 记一次Node和Go的性能测试 千兆网卡已经被打满了高并发
记一次Node和Go的性能测试 在helloworld场景下,若是咱们有万兆网卡,那么go的qps就是50000,go是node的7倍多,乍一看这个结果就很是有意思了,下面咱们分析下缘由:
首先node官方给的cluster的例子去跑压测是有问题的,CPU物理核心双核,超线程成4核,姑且咱们就认为有4核。 cluster的方案,先起主进程,而后再起跟CPU数量同样的子进程,因此如今总共5个进程,4个核心,就会形成上下文频繁切换,QPS异常低下 基于以上结论,我减小了一个worker进程的数量 Node 多进程模型下,1个主进程,3个worker进程 qps: 15000 (直接翻倍) 记一次Node和Go的性能测试 以上的结果证实咱们的想法是对的,而且这个场景下CPU利用率几乎达到了100%,不是很稳定,暂且咱们能够认为压满了,因此咱们能够认为1master4worker场景下,就是由于进程上下文频繁切换致使的qps低下
那么进一步想,若是把进程和CPU绑定,是否是能够进一步提升qps?
Node 多进程模型,而且用taskset命令把进程和CPU绑定了 qps: 17000 (比不绑定cpu性能提升了10%多) 记一次Node和Go的性能测试 结论 :node在把CPU压满的状况下,最高qps为:17000,而go在cpu利用率剩余30%的状况,qps已经达到了37000,若是网卡容许,那么go理论上能够达到50000左右的qps,是node的2.9倍左右。
CPU密集场景
为了模拟CPU密集场景,并保证两边场景一致,我在node和go中,分别添加了一段以下代码,每次请求循环100W次,而后相加:
var b int
for a := 0; a < 1000000; a ++ {
b = b + a
}
复制代码
Node 多进程模型:这里个人测试方式是开了4个worker,由于在CPU密集场景下,瓶颈每每在woker进程,而且将4个进程分别和4个核绑定,master进程就让他随风飘摇吧 qps: 3000 记一次Node和Go的性能测试 go:go不用作特殊处理,不用感知进程数量,不用绑定cpu,改了代码直接走起 qps: 6700,依然是node的两倍多 记一次Node和Go的性能测试 结论
Node由于用了V8,从而继承了单进程单线程的特性,单进程单线程好处是逻辑简单,什么锁,什么信号量,什么同步,都是浮云,老夫都是await一把梭。
而V8由于最初是使用在浏览器中,各类设置放在node上看起来就不是很是合理,好比最大使用内存在64位下才1.4G,虽然使用buffer能够避开这个问题,但始终是有这种限制,并且单线程的设计(磁盘I/0是多线程实现),注定了在现在的多核场景下一定要开多个进程,而多个进程又会带来进程间通讯问题。
Go不是很熟,可是前段时间小玩过几回,挺有意思的,一直听闻Go性能好,今天简单测了下果真不错,可是包管理和错误处理方式实在是让我有点不爽。
总的来讲在单机单应用的场景下,Go的性能整体在Node两倍左右,微服务场景下,Go也能起多进程,不过相比在机制上就没那么大优点了。
Node
优势
单进程单线程,逻辑清晰 多进程间环境隔离,单个进程down掉不会影响其余进程 开发语言受众广,上手易 缺点
多进程模型下,注定对于多核的利用会比较复杂,须要针对不一样场景(cpu密集或者I/O密集)来设计程序 语言自己由于历史遗留问题,存在较多的坑。 Go
优势
单进程多线程,单个进程便可利用N核 语言较为成熟,在语言层面就能够规避掉一些问题。 单从结果上来看,性能确实比node好。 缺点
包管理方案并不成熟,相对于npm来讲,简直被按在地上摩擦 if err != nil …. 多线程语言没法避开同步,锁,信号量等概念,若是对于锁等处理不当,会使性能大大下降,甚至死锁等。 自己就一个进程,稍有不慎,进程down了就玩完了。