記一次Node和Go的性能測試

之前簡單測過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_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示例代碼)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了就玩完了。

相關文章
相關標籤/搜索