内容简介:以前简单测过go的性能,高并发场景下确实比node会好一些,一直想找个时间系统性地测一下,手头正好有一台前段时间买的游戏主机,装了ubuntu就开测了Node(官网的cluster示例代码)首先开始并发量的测试,然而。。。游戏主机的CPU是i7-8700K,性能太强,以至于拿了两台mac也没能压满。。。
以前简单测过 go 的性能,高并发场景下确实比node会好一些,一直想找个时间系统性地测一下,手头正好有一台前段时间买的游戏主机,装了ubuntu就开测了
准备工作
- 测试机和试压机系统都是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_timestamps=1 $ sysctl -w net.ipv4.tcp_tw_reuse=1 $ sysctl -w net.ipv4.tcp_tw_recycle=1
-
测试 工具 我用的是siege,版本是3.0.8。
- 测试的js代码和go代码分别如下:
Node(官网的cluster示例代码)
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\n'); }).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也没能压满。。。
四处寻找一番,找到了好几年前花了千把块钱配的nas,上去一看是颗双核的i3-4170,妥了,这下肯定没问题
正式开始
跳过了小插曲,直接开测
I/O密集型场景
- Node - 多进程模型,可以看到因为所有请求都由master进程转发,master进程成为了瓶颈,在CPU占用100%的情况下,worker进程仅仅只占了50%,因此整体CPU利用率只有70%。
- qps: 6700
- Go - 单进程多线程模型,系统剩余CPU差不多也有30%,查了一下原因是因为网卡已经被打满了,千兆网卡看了下已经扎扎实实被打满了。
- qps: 37000
千兆网卡已经被打满了
在helloworld场景下,如果我们有万兆网卡,那么go的qps就是50000,go是node的7倍多,乍一看这个结果就非常有意思了,下面我们分析下原因:
- 首先node官方给的cluster的例子去跑压测是有问题的,CPU物理核心双核,超线程成4核,姑且我们就认为有4核。
- cluster的方案,先起主进程,然后再起跟CPU数量一样的子进程,所以现在总共5个进程,4个核心,就会造成上下文频繁切换,QPS异常低下
- 基于以上结论,我减少了一个worker进程的数量
- Node 多进程模型下,1个主进程,3个worker进程
- qps: 15000 (直接翻倍)
以上的结果证明我们的想法是对的,并且这个场景下CPU利用率几乎达到了100%,不是很稳定,暂且我们可以认为压满了,所以我们可以认为1master4worker场景下,就是因为进程上下文频繁切换导致的qps低下
那么进一步想,如果把进程和CPU绑定,是不是可以进一步提高qps?
- Node 多进程模型,并且用taskset命令把进程和CPU绑定了
- qps: 17000 (比不绑定cpu性能提高了10%多)
结论 :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
- go:go不用做特殊处理,不用感知进程数量,不用绑定cpu,改了代码直接走起
- qps: 6700,依然是node的两倍多
结论
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了就玩完了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 微服务测试之性能测试
- Go 单元测试和性能测试
- 性能测试vs压力测试vs负载测试
- SpringBoot | 第十三章:测试相关(单元测试、性能测试)
- Golang 性能测试 (2) 性能分析
- 随行付微服务测试之性能测试 原 荐
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web软件用户界面设计指南
林锐、唐勇、石志强 / 电子工业出版社 / 2005-5-1 / 20.00元
Web软件用户界面设计指南,ISBN:9787121010163,作者:林锐等编著一起来看看 《Web软件用户界面设计指南》 这本书的介绍吧!