精华 很久没来,丢一份前阵子做的 10 万连接性能测试 (fibjs, golang, nginx, nodejs)
发布于 9 年前 作者 xicilion 38298 次浏览 最后一次编辑是 8 年前 来自 分享

测试内容

动态HTTP服务器极限性能,分为两项:

  • 优化前后的各版本fibjs
  • 优化后的fibjs、使用cluster的nodejs、基于模块的nginx、使用多核的go

测试环境及方法

服务器

CPU:8核(4sockets*2cores),每核2.1GHz <br/> 内存:24GB

客户端(10个)

CPU:1核,每核2.1GHz <br/> 内存:1GB

测试流程:

  1. 首先在服务器端开启server
  2. 调度机ssh到客户端触发对服务器的wrk请求(测试时长为30min)
  3. 测试的并发量从1w~10w,递增幅度为1w

测试项一:fibjs优化前后性能对比

3个版本的fibjs,分别为:

  1. v0: 原版fibjs
  2. v1: 优化了SOCKET_ BUFF_ SIZE大小
  3. v2: socket 等待时释放 buffer 内存

测试结果

QPS(req/sec)

\ v0 v2 v3
10000 12750 15971 17705
20000 12757 15480 17893
30000 12551 15362 18045
40000 11858 15087 17326
50000 11678 15503 17237
60000 11708 15093 16800
70000 10682 14674 16513
80000 9395 14223 15840
90000 8733 13681 15209
100000 8645 13375 14664

QPS曲线如下图:

case1-QPS

内存占用(单位GB)

\ v0 v1 v2
10000 1.55 0.37 0.14
20000 2.95 0.68 0.22
30000 4.32 1.05 0.27
40000 5.82 1.32 0.35
50000 7.27 1.63 0.41
60000 8.79 1.89 0.47
70000 10.15 2.32 0.53
80000 11.57 2.63 0.6
90000 13.03 2.98 0.67
100000 14.5 3.36 0.79

内存占用曲线如下图:

case1-Mem

测试项二:优化后的fibjs与其他服务器性能对比

服务器分别为:

  1. 优化后的fibjs (v0.1.7)
  2. 使用了cluster的nodejs (v0.10.25)
  3. 使用多核的go (v1.5.1)
  4. nginx模块 (v1.9.2)

测试结果

QPS(req/sec)

\ fibjs nodejs go nginx
10000 17705 8268 22284 19393
20000 17893 5866 21975 19096
30000 18045 4412 21531 18521
40000 17326 2817 21362 18331
50000 17237 2179 21277 18328
60000 16800 1853 21113 17936
70000 16513 1536 21029 17644
80000 15840 1385 20497 16973
90000 15209 1232 19843 16966
100000 14664 1089 19386 16805

QPS曲线如下图

case2-QPS

内存占用(单位GB)

\ fibjs nodejs go nginx
10000 0.14 0.56 0.37 0.1
20000 0.22 0.68 0.68 0.1
30000 0.27 0.76 1.05 0.11
40000 0.35 0.84 1.32 0.12
50000 0.41 0.92 1.63 0.12
60000 0.47 1.02 1.89 0.12
70000 0.53 1.08 2.32 0.13
80000 0.6 1.14 2.63 0.13
90000 0.67 1.3 2.98 0.14
100000 0.79 1.45 3.36 0.14

内存占用曲线如下图

case2-Mem

服务器CPU占用情况

\ fibjs nodejs go nginx
CPU占用率 500~600 (90~98)*8 200~400 (30~35)*8

结论

模型

  • fibjs和go均为单进程多线程 <br/>
  • nodejs(cluster)和nginx是多进程

内存占用

  • go平均每个请求所需内存最多,约为320KB
  • fibjs和nodejs平均每个请求所需内存相差不多,均约为72KB,不过nodejs占用的固定内存多于fibjs
  • nginx采用了Zero Copy技术,内存占用非常低

QPS

  • nginx和go都未跑满CPU,且在高并发下表现良好
  • fibjs的http协议栈处理是多线程的,且CPU占用率高于nginx和go,在高并发情况下表现良好
  • nodejs在3w并发时已基本跑满CPU,且性能下滑严重

相关代码

服务器代码1_fibjs

var http = require('http'),
	net = require('net'),
	coroutine = require('coroutine');

var interval = 1000;

var hdlr = new http.Handler(function(req) {
	if (req.address == "/fibjs") {
		req.response.write('Hello, World!');
	}
})

var demon = function() {
	while (true) {
		console.error("connections:", svr.stats.connections,
			"\trequest:", hdlr.stats.request,
			"\tresponse:", hdlr.stats.response);
		hdlr.stats.reset();
		svr.stats.reset();
		coroutine.sleep(interval);
	}
}

var svr = new net.TcpServer(8080, hdlr);

coroutine.start(demon);
svr.run();

服务器代码2_使用cluster的nodejs

var cluster = require('cluster'),
	http = require('http'),
	url = require('url'),
	numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
	console.log("master start...");

	// Fork workers.
	for (var i = 0; i < numCPUs; i++) {
		cluster.fork();
	}

	cluster.on('listening', function(worker, address) {
		console.log('listening: worker ' + worker.process.pid + ', Address: ' + address.address + ":" + address.port);
	});

	cluster.on('exit', function(worker, code, signal) {
		console.log('worker ' + worker.process.pid + ' died');
	});
} else {
	http.createServer(function(req, res) {
		var pathname = url.parse(req.url).pathname;
		if ("/node" === pathname) {
			res.writeHead(200);
			res.end("Hello World!\n");
		}
	}).listen(8080);
}

服务器代码3_使用多核的go

package main
import (
    "net/http"
    "runtime"
    "log"
)

func hdlr_hello(rw http.ResponseWriter, req *http.Request) {
    req.ParseForm()  
    rw.Write([]byte("Hello world!"))
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    http.HandleFunc("/go", hdlr_hello) //设置访问的路由
    err := http.ListenAndServe(":8083", nil) //设置监听的端口
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
        // panic(err)
    }
}

服务器代码4_nginx模块

#include <ngx_config.h> 
#include <ngx_core.h> 
#include <ngx_http.h> 
#include <ngx_buf.h>

/*
 *cf   指向ngx_conf_t 结构体指针,从指令后面传过来的参数 
 *cmd  指向当前结构体ngx_command_t 的指针(互相指) 
 *conf 指向自定义模块配置结构体的指针 
 */
static char *ngx_http_hello_world(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 

static ngx_command_t ngx_http_hello_world_commands[]={ 
    { 
        ngx_string("hello_world"),          //指令名称,nginx.conf中使用 
        NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,  //注释1 
        ngx_http_hello_world,               //回调函数,上面申明 
        0,                                  //保持的值存放位置:全局,server,location 
        0,                                  //指令的值保存位置 
        NULL                                
    }, 
    ngx_null_command  //读入ngx_null_command 指令后停止 
}; 

//ngx_http_<module name>_module_ctx用于创建和合并三个配置 
static ngx_http_module_t ngx_http_hello_world_module_ctx={ 
    NULL,   //preconfiguration 
    NULL,   //postconfiguration 
    NULL,   //create main configuration 
    NULL,   //init main configuration 
    NULL,   //create server configuration 
    NULL,   //merge server configuration 
    NULL,   //create location configuration 
    NULL    //merge localtion configuration 
};

//nginx进程,线程相关,ngx_http_<module name>_module把数据处理关联到特定模块
ngx_module_t ngx_http_hello_world_module={ 
    NGX_MODULE_V1, 
    &ngx_http_hello_world_module_ctx,   //module context 
    ngx_http_hello_world_commands,      //module directives 
    NGX_HTTP_MODULE,                    //module type 
    NULL,	//init master 
    NULL,	//init module 
    NULL,	//init process 
    NULL,	//init thread 
    NULL,	//exit thread 
    NULL,	//exit process 
    NULL,	//exit master 
    NGX_MODULE_V1_PADDING 
};

static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t *r) 
{
    if(!(r->method & NGX_HTTP_GET))
        return NGX_HTTP_NOT_ALLOWED;
 
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if(rc != NGX_OK)
        return rc;
 
    ngx_str_t type = ngx_string("text/plain");
    ngx_str_t response = ngx_string("Hello World!");
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = response.len;
    r->headers_out.content_type = type;
 
    rc = ngx_http_send_header(r);
    if(rc == NGX_ERROR || rc > NGX_OK || r->header_only)
        return rc;
 
    ngx_buf_t *b;
    b = ngx_create_temp_buf(r->pool, response.len);
    if(b == NULL)
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
 
    ngx_memcpy(b->pos, response.data, response.len);
    b->last = b->pos + response.len;
    b->last_buf = 1;
 
    ngx_chain_t out;
    out.buf = b;
    out.next = NULL;
        
    return ngx_http_output_filter(r,&out); 
}

//回调函数,1获得location中的“核心”结构体,2为他分配个处理函数 
static char *ngx_http_hello_world(ngx_conf_t *cf,ngx_command_t *cmd,void *conf) 
{ 
    ngx_http_core_loc_conf_t *clcf; 

    clcf = ngx_http_conf_get_module_loc_conf(cf,ngx_http_core_module); 
    clcf->handler = ngx_http_hello_world_handler; 
    return NGX_CONF_OK; 
} 

###脚本_集群调度

#!/usr/bash

if [ $# -lt 1 ]; then
	echo ""
	echo "option [conn | close]"
	echo ""
	exit 0
fi

user=verdant
concurrency=50000
duration=600
logdir="log/"
logfile="ctrl.log."
ip_array=("171" "193" "209" "213" "231" "219" "222" "223" "224" "225")  
conn=$(($concurrency/${#ip_array[*]}))
sleeptime=1
# sleeptime=$(($concurrency/10000+1))

remote_cmd="~/wrk -t10 -c$conn -d""$duration""s http://192.168.1.61:8080/fibjs"
# remote_cmd="~/wrk -t10 -c$conn -d""$duration""s http://192.168.1.61:8080/node"
# remote_cmd="~/wrk -t10 -c$conn -d""$duration""s http://192.168.1.61:8080/go" 
# remote_cmd="~/wrk -t10 -c$conn -d""$duration""s http://192.168.1.61:8080/nginx"

function main()
{
	echo "${#ip_array[*]} client each: $conn"
	for ip in ${ip_array[*]}  
	do  
		if [ -e "$logdir$logfile$ip" ]; then
			rm "$logdir$logfile$ip"
		fi	   
	done

	if [ $1 = 'close' ]; then
		for ip in ${ip_array[*]}  
		do  	 
	    	result=$(ssh -t -t $user@"192.168.1."$ip "killall -9 wrk">> $logdir$logfile$ip &)
	    	sleep "$sleeptime"
	    	echo $result
	    done
		exit 0
	fi

	if [ $1 = 'conn' ]; then
		for ip in ${ip_array[*]}  
		do  	 
	    	ssh -t -t $user@"192.168.1."$ip "$remote_cmd">> $logdir$logfile$ip &  
	    	sleep "$sleeptime"
		done 
	fi
}

function recovery()
{
	running=0
	server_ok=()
	while [ $running -ne ${#ip_array[*]} ]
	do
		for ip in ${ip_array[*]}  
		do  	
			if [ ! -s "$logdir$logfile$ip" ]; then 
				echo "restart:""$logdir$logfile$ip"
				sleep $((5*$sleeptime))
				ssh -t -t $user@"192.168.1."$ip "killall -9 wrk"
				sleep $((10*$sleeptime))
				ssh -t -t $user@"192.168.1."$ip "$remote_cmd">> $logdir$logfile$ip &
			else
				if [[ "${server_ok[@]/$ip/}" = "${server_ok[@]}" ]]; then
					server_ok[${#server_ok[@]}]=$ip
					running=$(($running+1))
				fi
			fi
    	done
    done
}

function calculate()
{
	result1=0
	result2=1
	while [ $1 = 'conn' -a $result1 != $result2 ]
	do
		result1=`cat $logdir$logfile*|grep Requests/sec|awk '{print $2}'|awk '{sum+=$1} END {print sum}'`
		if [ "$result1" = "" ]; then
			result1=0
			sleep 10
			continue
		fi
		sleep 10
		result2=`cat $logdir$logfile*|grep Requests/sec|awk '{print $2}'|awk '{sum+=$1} END {print sum}'`
	done
	echo "Requests/sec: $result1"
	cat $logdir$logfile*|grep Latency
}

main $1
sleep $((${#ip_array[*]}*$sleeptime))

#客户端异常重连机制
if [ $1 = 'conn' ]; then
	recovery  
fi

sleep $duration
echo "calculate..."
calculate $1
94 回复

node用0.10、、、、、

@i5ting 会有差别吗?

@xicilion 会的,用4.2或5.x

@i5ting 估计会有多大差别?提升 100% 还是提升 1000%?提升 100% 以内就不麻烦再测试一遍了。

然而生态、、、、、

不可否认node的确曾经依靠纯异步有不错的性能表现,但现在要讲高性能,这得看跟谁比了,跟同步比那还能算高性能的,但跟其他异步框架比起来,还能说是高性能么?从测试成绩来看,顶多算还行,就算用4.2,5.x来测,结果也好看不到哪里去,至少不会出现数量极的改变。

现在node社区的人,唯一可以自慰的就是生态了,为什么会这样?因为性能已经不好意思拿来吹了。

感谢响马大哥的测试。

支持国产是重点

定位要准确,没有一种语言是万金油,选择适合自己的

fibjs 在牛X再好,我也不会去学习去使用用,也不会叫同事去学习去使用,只是一年内不会,等以后用的人多起来再观望。 在一个团队或者项目中选择要使用fibjs,不但要看团队里面的使用的人多不多,还要看外面用的人多不多,否则招不到人也是悲剧。 当然,本着追求探索的目的,玩玩还是可以的

nodejs 代码里 var pathname = url.parse(req.url).pathname; 这句可以省掉,直接用"/node" === req.url

很详细很赞的测试,我只能膜拜了。 说说几点我的观点吧。

  1. 不管是fibjs, nodejs, go或者其他工具,在大规模以及大项目中使用不会只有一台服务器在跑,都会有相应的分流来均衡.
  2. 追求性能的提升当然是件好事也是对各种技术的挑战以及自我的挑战,但是很多小公司小项目或者中型项目,在没有遇到性能瓶颈的时候 我觉得没有很大的必要去追求高性能高并发. 我这里只是对那些小公司小项目说的。(楼主对技术的追求还是很赞的)
  3. 性能瓶颈往往是出现在数据的存储以及调用方面以及磁盘IO

对楼主还是敬佩的。上面所列的几点只不过我站在客观角度所言。

做这个测试的原因当然是出于是业务需要,有一些业务场景是需要维持大量连接而运算量并不大的。

测试过程中也有两次对 fibjs 优化的结果。

最初并没有打算测试 nodejs,因为知道它肯定抗不住。但是考虑到发出来肯定有人会问 nodejs 的数据,就特意测了一份来。做出来才知道,nodejs 比我能想象的更差劲。一万以内,fibjs 是它的 2 倍,到 10 万的时候,已经是它的 15 倍了。就这点性能还启用了 cluster。

至于招人,还真不难,校招新人一周内上手,比 nodejs 快多了,而且基本不会写错代码。

@flyingcodes

fibjs的思想还是非常值得学习的,利用协程隐藏回调,实现形式同步,实质异步。形式同步是大势所驱,编程模型应该越简单越好,这不仅仅对nodejs,对其他所有技术都一样。@xicilion 大哥的公司能够在大学里招人过来,一个礼拜上手,本身就说明了这点。学校里教的就是同步编程,业务大多也是同步需求。

显式的异步代码对异步本身的发展就是阻碍。我们只需要异步带来的性能好处,但并不需要由此引入的编程复杂度。

@xicilion 为何还不干掉go那个我讨厌的语言

@coordcn 虽然我掉进了回调的坑,但是我觉得掉的还蛮值得的。不管代码层面异步还是同步,我个人觉得没关系,而且通过异步代码的编写我多少了解了一点异步回调隐藏的方法。如果fibjs雄起甚至干掉nodejs也是蛮好的。如果确实性能突出, 而且生态好的话,必须用!大大们多做做实验吧~撇开这个测试不说,希望响马能多广告发扬一下fibjs,让广大编程工作者来检验一下,要是真把nodejs屁股给踢疼了,我觉得应该很赞!

@coordcn 首先我也肯定fibjs另一种开发思想和代码风格,我只是现在不看好fibjs在其他公司的应用,比较了解的人少,深入的人估计只有@xicilion自己, 至于 @xicilion敢招大学里面的新手,那是因为他自己了解,其他人敢在他们公司的项目里面使用?然后也去学校里面招人?

@DevinXian

对异步理解到了一个程度,异步的形式已经不重要了,这个时候追求的应该是非显式的异步,就像武功高强的人达到无招胜有招的境界。很多原来同步的语言,现在通过改进后,nodejs已经没有任何性能优势了,如果继续抱着原有的编程模型不放,只会越来越落后。

node在阿里的应用也仅仅限于大前端,阿里的人自己在之乎上说核心交易nodejs成绩为0。这里的原因,我想,java成熟稳定是一方面,nodejs显示异步的编程模型和业务同步逻辑之间的矛盾才是最主要的。核心交易业务复杂的程度,不是简单的理解了回调,理解了异步就能应对的。你只要自己试着用nodejs写一个异步文件夹遍历,就可以想象在复杂业务条件下,显示的异步是多么的无力。

回调不能代表异步,如果你愿意,同步代码也可以用回调来表达。回调在异步程序中的作用恰恰是保证同步,保证执行顺序的。

回调模式下的异步,现在已经成为nodejs的包袱,generator和promise的缓和作用有限,学习成本还是有的,是典型的为了解决一个问题,又引入另外一个问题的做法。这些东西在前端是适用的,但面对复杂的后端,形式同步才是终极解决方案。

nodejs用来做demo,做一些大前端页面渲染是合适的,这些应用不涉及复杂的后端数据库交互过程,只要一两次异步就能搞定,阿里就是这么搞的,真正的后端将数据准备好,nodejs取过来,套上模板渲染传给浏览器。

@coordcn 多谢鸡汤。PS:你撸汉字快赶上我撸代码速度了0.0

@flyingcodes

fibjs的源代码我大致浏览过,实现思路也大致了解,细节没有深入。我更看重的是思路,有了思路,代码堆出来只是时间问题。

我自己也在利用libuv和lua实现一个形式同步的网络库,做这个事情,是去年受到fibjs的触动,我那时正好在写文件夹遍历,被回调搞得很爽,这个过程我就有了反思,后来也看了openresty的代码,我c++不行,fibjs,v8都玩不转,就选择了c + lua + libuv,lua天生提供协程支持,openresty又有现成的通过协程回调转同步代码参考,lua和javascript在本质上很相似,libuv读nodejs的源代码的时候看过,自己很喜欢,自然就用这个网络库了。整个框架已经搭好,中间重构了一次,马上就可以发布出来。

@DevinXian

那你写代码速度好快了,我除非写测试代码,不然一天也就顶多200行不到。

@coordcn 赞,期待中。。。

这样看go这么牛逼,要性能那直接上go,至少生态要好啊。

有对比,对语言,还是社区都是好事。 @xicilion 可否有空翻成英文,发到 HN 等,看看社区反映。

fibjs确实不错,目前还属于极客,还需要时间吧。

@coordcn 哈哈,你自己都说了你c++不行,那么出了bug你解决不了,我没有关注fibjs,只关注过node.js,但深入使用了node.js就发现很多bug,其中大部分是由于c++(包括node的核心库和v8引擎的)码引起的,所以即使深入使用node了都会引起问。

@xicilion

跟响马大哥的fibjs是没法比的,整个项目还很原始,但是通过协程异步转同步的思路还是表达出来了。fibjs的让我对异步有了更进一步的认识,同步代码才是异步程序的最终归宿,编程思想才是最重要的,非常感谢引路人响马大哥。

我c++水平太次,只能看懂代码,自己写不行,无奈只好用c+lua来实现,无法为fibjs做什么贡献。

其实你们可以把 fibjs理解为一个能运行v8的框架,这个框架包括了文件数据库网络等等的东西 node理解为一个封装有常用系统api和其它一些常用类库的服务端用的js运行时 只不过js引擎用的v8 其实也可以换成其他的比如微软就换成查克拉了 其它有些人换成spidermonkey了 nodejs 和 fibjs 差不多的地方就是都碰巧选用了v8而已

@jiangzhuo fibjs 选 v8 倒不是碰巧,spidermonkey 和 JavascriptCore 都不能很好地支持 fiber,只有 v8 能够兼容这个模式。微软的引擎向来支持多线程,应该可以使用,但是跨平台是个问题。

因此 fibjs 最终选择了 v8。nodejs 对引擎依赖比较小,倒是可以很方便移植,几个 Javascript 引擎都有移植的版本。

@coordcn 看是什么代码了,哈哈,我指的其实是字符数!

我想知道nodejs以后有没有性能提高的可能,在现有架构下,毕竟前端工程师不想放弃npm丰富的支持

fibjs好像很有趣。我去看看。

  1. 我试过在ubuntu lts 32bit 上编译,每次都是段错误。
  2. 在vs2015社区版无法按说明正常编译(非要用旧版本vs才可以么)。
  3. 希望能提供二进制版本下载
  4. 官网文档有点…(谈一些喜欢喝什么的问题真的好么)

与其唱双簧,还是希望能把官网的引导做好一点。(官网打开很慢,jquery的content-download时间20s,也不知道是网络问题还是服务器问题还是我的浏览器爆炸了)

用nodejs其实没别的,就是生态太完善了,想要的库基本上都有,再加上性能也还可以。最大的问题就是异步。成也异步败也异步,不过现在也已经有比较好的方案再解决这个问题。至于性能,很多时候说实话不是最关键,初创企业能有几个项目真能一开始就碰到太多性能问题,开发效率和速度才是最关键啊。

统一回复:

@dlutwuwei 没有可能。

@lingmm 一、中间有几个版本在 32 bits 上运行是会出错,我们没做好全平台测试,一直也没有人回报,目前已经发现并修复了 二、vs2015 稍微转换即可编译,而设置为 vs2015,vs2013 却肯定不能编译了。windows 版本仅为兼容,虽然性能同样很好,但是我们自己并没有大量使用,虽然我知道有公司在 windows 上投入生产环境,但是我们没有一手数据,所以不建议生产环境使用,做做测试尚可 三、人力所限,不提供二进制下载,如果是 mac,可以通过 brew install fibjs 直接安装 四、文档是萝卜青菜,各有所爱,大家都是程序员,就别在那些虚头巴脑的文字上纠结了,直接干代码就好了 五、官网慢是因为 vps 慢,建议从 github 下载 docs 本地阅读,国内 vps 还要备案,烦躁

@arden 文无第一,武无第二,大家都是程序员,快就是快,不快就是不快。你看我就从来不辩解说生态不重要,没有就是没有。

我记得我去年在这里就看到,楼主发测试了。今年又是测试。。。对,fibjs快!

楼主大神,弄的我都不想学node了,好样的

佩服楼主的功力,但是还是不看好。

每次看到楼主的帖子就想放弃nodejs,还是安心做一个前端吧,想当初在java的泥潭里受不了,转前端也就是因为nodejs才下定决心

@dlutwuwei 为啥要放弃,是因为你觉得楼主太厉害了吗。。。

这种贴,评论永远比正文更精彩

@xicilion 我觉得既然你们在这里发贴子,肯定也有想推广的心吧。能够看到更多的人使用才能证明他开源的成功不是。不然自己藏着就好了。。既然说文档不重要,那多做点demo啊=。=

@lingmm http://fibjs.org 有全套文档,代码目录的 test 内有每一个对象与模块的每一个 api 的使用用例。

这次对比测试后,fibjs 又进行了几个版本的迭代,这是历次迭代后的性能提升。这些提升都不是通过优化 http 逻辑得来,而是重构 fibjs 基础对象系统获取,因此对所有模块均有提升。 Screen Shot 2016-01-16 at 4.34.48 PM.png Screen Shot 2016-01-16 at 4.36.55 PM.png

什么时候分享一下测试的东西

又见hello world测试。。。毫无意义。。。

你提到了go.可以去试试这个,https://github.com/valyala/fasthttp 性能是标库http的5倍以上。 但有什么意义?

@alanyang

别人的工作怎么会毫无意义呢?一些人总喜欢纠结于测试本身的结果,而忽略了为什么会有这样的结果,也就是它快,但为什么这样快呢?

@xicilion 不但给出了具体的测试方法和代码,还告诉大家优化的过程和思路,这个过程和思路才是真正的精华所在。服务器优化到极至,拼的就是内存的使用效率,不该分配的地方绝对不重新分配,不该拷贝的地方绝对不做无谓的拷贝,可以重用的尽量重用。这些方法其实都是通用的优化方案,这些工作我认为是非常意义的。

fasthttp优化的原理也是大致相同的,同样是减少重新分配,少拷贝,多重用。

fibjs和go相对于nodejs而言,更重要的意义就是使得nodejs不得不朝更加易用的形式同步编程模式演化,把回调异步的神话打破。想当初nodejs是多么痴迷回调异步,拒绝引入协程,结果活生生被ES6打脸了,还闹出了分裂逼宫的闹剧,要想舒舒服服的写异步程序,协程是绕不过去的,即便是async/await还很低级,更不要说ES6了。当有的人看到一些人老早就在用同步代码写异步程序的时候,你就会觉得有的人的工作是意义大去了,这个意义不在代码本身,而在思想。

你所做的性能改进为什么不在nodejs上做呢?走分裂的路不对,添砖加瓦才有前途。

@gzhangzy 根本不是一个路线,没办法添啊。况且fibjs刚出来的时候,nodejs回调异步还如日中天,人根本看不上协程这种路线,虽然现在nodejs也事实上走了类协程路线,最终还是会发展成跟fibjs类似的形式同步,实质异步。nodejs加个新特性都要闹分裂,逼宫,fibjs这么激进的方式他们根本不会接受。

很奇怪,go语言有这么多如此优秀的框架,为什么网上总是有人批判go语言不好。

感觉fibjs在javascript语言里很牛逼啊,但总觉得在这里贬低nodejs没有什么用

我想问的是,前端web界面里面,ajax,可以用这种"协程", 的方式来编程吗? 很多应用,前端也是有很多逻辑的

@151263 http://blog.csdn.net/kobejayandy/article/details/11856735 这个是“协程”的概念的,node里面有async支持协程,前台里面应该是promise这个库来支持的吧(不知道理解的对不对o(╯□╰)o)

@coordcn 我还是坚持我的观点,hello world无意义。纯浪费时候。。

最少加点template render, session in cookies之类cpu运算。 最少得加点io操作模拟可能的阻塞吧。

这种测试和http paser + epoll测试有什么区别?

有个小故事,go界有个最近吹得很厉害的框架iris,自称性能秒杀标库http。。当然他自己测试用的也是hello world :) 后来有人加了些许线上常出现的业务代码,性能下降到连martini不如,沦为笑柄。 后来看代码才知道,这货是取巧缓存了context,当无阻塞时候,那当然快得很。但线上情况可能吗?

对于你说的引入coroutine的优势,还是认可的。

@arden 语言太朴实,没花头。。

@alanyang 无话可说,不谈性能,不谈生态,就是喜好~

http://www.jdon.com/47912 这篇文章说,“协程”, 是有问题的

—有时,回调函数反而更加强大,虽然,goroutine协程口口声声说是用来替代万恶的回调函数的。协程只是比线程较为轻量,但是较为轻量不代表它是最轻量的。

@alanyang

我已经说过了,高性能服务器在不动内核的前提下,大家最后比的就是内存使用效率,同样原理,不同的代码,性能差距还是有的,helloworld就是为了排除其他影响,测试纯粹的网络性能的。这个东西不能代表性能的全部,但也是一个比较重要的指标。nodejs当初不也是helloworld起家的?

个别人做helloworld测试不严谨,投机取巧,甚至作弊,也不能证明helloworld测试性能就毫无意义啊,这个基本的逻辑关系,我想程序员都应该能够搞清楚的。你如果看过V8的源代码,就会明白缓存是一个非常重要的优化手段,不必要的计算不计算,这本身就是一种非常重要的优化思想。

我们需要谴责的是那种为了数据漂亮,不择手段的测试,像响马这样的测试,我觉得不是属于这种。但必须要明白,这些测试也仅仅对测试代码负责,并不能代表全部性能。框架性能再好,也架不住业务代码过分烂啊。但如果框架性能好点,业务代码的性能压力相对要小一些。

@151263

协程相对于纯粹的回调性能会差点,协程切换还是要保存CPU寄存器的,跟函数调用的代价比还是比较大的。

协程比回调优势的地方是在异步表达上,协程可以实现用同步代码编写异步程序,回调是无法做到的。

性能和编码效率在这里就是一对矛盾,现在已经证明纯粹的回调编码效率不行,javascript引入了generator/yield,async/await来缓解这个问题,但由于这两者背后还是基于回调,虽然代码局部同步了,但有的时候还是要用异步思维去写程序。javascript在纯粹的回调外又包装了一层,其性能很难说比协程有优势了。

我用C + lua5.3 + libuv 实现的异步框架luaio,TCP性能就比nodejs高一倍,luaio可以用完全同步的代码写异步程序。所以虽然理论上纯回调性能更好,但具体实现还要看实际情况。协程实现方法也有很多种,性能也不同。lua的是单线程setjmp/longjmp,fibjs是自己汇编实现的ucontext,go是多线程的。

选择什么样的异步编程模型,归根到底还是在编码效率和性能之间做个权衡,没有完全无懈可击的方案,只有适合自己的方案。

有空研究下fibjs的底层实现 昨天也是偶然看到一个回复提到了fibjs,确实大开眼界

估计会有多大差别?提升 100% 还是提升 1000%?提升 100% 以内就不麻烦再测试一遍了。

用 0.10 做对比基本没什么参考价值,反倒误导了不少不留意的同学

goroutine协程本质上也是解决 io 密集型任务。其实就实现角度看,回调是基础,回调技术包含着闭包技术,闭包保存了打断的上下文,使得逻辑可以继续。现在 es6 的 generator 从 js 层面实现了,也可以从C++层面做这些事情,我觉得 fibjs 就是在 C++层面实现了,因为将异步代码写成同步的是大势所趋。

之前关注过fibjs,也非常看好它。很多小伙伴都有体会,从接触nodejs开始,大部分精力都耗费在如何通过同步的方法来写异步代码上了,现在连js都在语言层面来适应它,实在拧巴。fibjs从底层解决了这个问题,应该是很好的解决思路。有机会一定认真研究下fibjs,把亿书代码用fibjs实现一遍。

感觉又一波可以学

伟大的楼主,是否可以考虑再并行加上一个J2EE的跑分评测结果呀?这样的话,我就能对go, fibjs, nginx, J2EE有一个比较全面的评估了。还是说,J2EE跑分结果太低了,都不合适写到贴子里,再耽误功夫了。

文档感人,如果不把文档完善下的话,我不晓得有什么人敢用

fibjs支持server端的canvas(类似于node-canvas),我的项目里有需求:在后端合成图片。

大脸叔叔很厉害

node 应用范围确实要注意哦, 如果国内有商家支持、咱们再给力积攒下生态fibjs 有希望哦;

有点理想化了

这都什么年代,还拿个 v0.10.25 测试,现在 v8.0 都出了。

要不然,大家一起学Rust吧。Rust是非虚拟机语言,比Go还快。当然,如果你写Cgo的话,性能会与Rust持平,但是,《内存安全》又会成为Cgo相对于Rust的短板。此外,Rust和js一样,既能运行在浏览器里,也能运行在server端。

应该用nodejs 8.x.x 做个测试

虽然是2年前的,这确实是一个不公平的测试,跟其它测试 req.url 实现一样用字符串比较吧。 url.parse() 是300多行这么复杂的一个函数:https://github.com/nodejs/node/blob/master/lib/url.js#L102

@pinxue

fibjs也要做的URL解析的。

node性能没有优势是事实,不服气的话可以自己做测试验证下。你可以跟踪下热点,验证下你说的300多行JS到底是不是性能热点。

@coordcn var hdlr = new http.Handler(function(req) { if (req.address == “/fibjs”) { req.response.write(‘Hello, World!’); } }) 没看过 fibjs,请问是怎么个解析法?

另外,您跟踪过?贴出来省我点儿时间呗?

@pinxue 这个时间我不能帮你省,疑问是你提出来的,要证明自己,就得自己去求证。看看fibjs的address有没有解析,然后跟踪下URL解析过程到底是不是性能热点,如果fibjs做了解析,URL解析也不是性能热点,那你认为你的担心还成立么?

node性能现在跟go,openresty比落后,异步编程模型也落后。行就是行,好就是好,落后就是落后,不会因为你喜欢就会改变。

node安心做好前端工具,做好大前端,做点创业原型项目就行了,性能没有优势不是什么大不了的。node强在生态,强在群众基础,性能不差就行。

@coordcn 同意楼上观点,阿里也在搞个蛋,看nodejs周边web框架多如毛,其他东西呢?大前端?

@coordcn 那 fibjs 到底做了 url 解析没有呢?

@pinxue 源代码就在那里,你自己可以去求证到底有没有解析,如果你认为响马作弊了,你可以给出一个公平的测试让大家信服,你也可以联系响马跟他沟通。

我已经说过了,首先要看有没有做解析,然后还要看这个解析是不是性能热点,你要有测试数据来支撑,不是想当然的认为那个300行代码就是热点,你要用你的测试来支持你的观点。

node性能没有优势是事实,跟go,openresty比都差。node的优势在性能不差的前提下,生态和群众基础比go和openresty要好。node做前端工具挺好,我自己也用vuejs,前端工程化,模块化,离不开node;做大前端也不错,node负责渲染,其他语言如java提供json数据;创业公司做项目原型,中小型项目,只要有前后端通吃的牛人,node也是适合的,当然有些牛得不行得人,也可以用node做大型项目。这是node的优势,但前提是你得有牛人,真正前后端都牛的人很少,这个论坛都找不到几个。用node的公司大多是创始人或技术负责人是node牛人,如果没有这个前提,用node根本不如php(性能已经赶上了),java(人好找),openresty(lua的学习成本远低于javascript,完全用同步代码写异步程序)。go开发效率不如node,但go我个人认为做一些稳定的,有性能要求的中间层比较合适,做web接口纯粹浪费。node做go的活也不合适。

fibjs也不可能替代node,生态虽然可以用npm的,但为了追求性能,太多的东西用c++,这个论坛里搞javascript的会C++的有几个?能做贡献的人很少。协程实现也是用hack方式实现的,思想上很先进,但我不认可这种实现方式。fibjs只适合有响马这种大牛的公司。

我上面说这么多,你认为node性能差一点还有那么重要么?公司里有个牛人带,远比那么点性能重要得多,创业公司有几个并发吃不消的?真吃不消了,加机器合算还是加人合算?

node最大的问题也就出在这牛人身上,前端的大牛工资比后端高,除了兴趣,为什么要朝后端转?他前后端通吃了,价格就不会便宜,你请他一个人的价钱,就超过两个前端和两个后端了。他前后端通吃了,他就自己去创业了。大牛都去做CTO,去大公司了。事实上前后端知识结构和思维差异还是很大了,整天吹嘘前后端通吃的未必是大牛。

选语言,选框架,性能只是一个因素,自己喜欢才是王道。

@coordcn 也就是说,这个测试的细节,你啥也不知道,扯些不相干有啥用?

顺便,坛子里会 C++ 的有几个不清楚,反正我二十年前就会了。fibjs 虽然没用过,不过这个世界上熟悉传统线程模型和协程的人很多,想来并不需要“大牛”才能玩得转。

@pinxue 质疑响马测试公平的是你,不是我,你难道坚持认为我必须要为你的质疑找证据?如果这样,你的话我也可以原封不动的还给你,你搞清楚了,你给出让大家信服的测试和数据就行了。

大话谁都会说,会C++,懂协程搞个fibjs出来看看?产品做不出来,搞个原型也可以,并发性能要求不高,跟node持平就行。

我上面的要求就是顺着你的逻辑给的,你提出质疑,未验证,我提出验证方法,所以这个质疑的验证责任就成我的了?那你说你很牛,我表示质疑,要求你实现个原型来证明下自己不为过吧。这个质疑实际上毛用没有,你不证明也不能说明你水平不行,我的质疑无法验证。同样的道理,你的质疑也未验证,我给出方案验证,首先看下fibjs有没有解析,然后看下解析是不是性能热点,如果fibjs没有解析且解析的确是性能热点,那这个测试的确不公平,你给出数据,自然就推翻了响马的测试。

谁质疑谁举证,fibjs的代码我看过了,至于结论要你自己来说明。有没有解析?解析是不是性能热点?你那么牛,这应该不难吧。

@coordcn shut up and show me the code.

@pinxue 你要我show什么code给你呢?带你去读fibjs源代码?你不是有20年cpp功力,我只是看得懂,我不会写cpp的。

C语言我倒是能算得上会的,也搞了个类fibjs和openresty的项目,就不知道你赏不赏脸去see一下。

@coordcn show fibjs 的例子在哪里 parse 了那个 url。

@pinxue 质疑是你提出来的,代码不得你来show,正好show下你20年的cpp功底。

再次提醒你,fibjs的代码我肯定是看过了。有没有parse,parse代码是不是性能热点要你自己去求证测试。这个道理你不会还不明白吧,谁质疑谁举证。

@coordcn 我指出响马的测试代码里,Node.js 的版本跟 fibjs 和 go 的版本实现有差别,并给了 url.parse() 实现的具体代码。你质疑我的问题,号称 fibjs 也做了处理,请问,代码在哪里?

@pinxue 请问你认真阅读过node那300行代码么?你阅读过fibjs的代码么?你怎么有勇气来质疑的?代码不是以行数看性能的。叫你去做性能热点分析你又不做,读代码也不读,就连你自己给的链接也没弄清楚到底会执行几行代码。

再提醒一点,响马喜欢用cpp实现一切,你有20年cpp功底,找到这些代码不是难事吧。

我还可以负责任的告诉你,node的parse其实执行了两次,一次在httpparser,一次就是你给的代码。人家响马只做了一次。这是不是也要怪响马?

你要真牛逼,就把node代码通读一遍,libuv,httpparser。做完这些功课再来跟我牛。

@coordcn 真是喷了,var pathname = url.parse(req.url).pathname; 这行代码是 node.js 的? 你扯了一堆有的没的,合着连我在说什么都没弄明白……

@pinxue 真是服了你了,自己没弄明白fibjs的address和node的url的区别,这两者不是同一个东西,fibjs已经解析好的,node是没解析过的。node当然得再解析一次,自己去好好看看node的http文档吧。

叫你去读node源代码你又不去读,光说自己20年cpp的底子,自己去读了源代码不就明白了?httpparser里面对url做了一次解析,但只是做验证字符合法性的,没有将解析结果传给js,所以必须在js层再解析一次。fibjs在cpp里做了解析,address就是node解析的pathname。这些功课你都不做,你怎么好意思来质疑的?

@i5ting 一个精华帖子就这么雪藏了?你老还是多带带小弟读读源代码,不然是要闹笑话的。

node社区太浮躁了,一个个都牛得不行,其实水得可以。真应了我那句话,大部分所谓全栈其实是前端不强,后端不行。

@coordcn 大哥们多支持啊,精力不够用啊

@coordcn benchmark 公平基础就是用一样的代码来跑,至于系统内部处理了还是没处理,是分析 benchmark 结果原因时才用得上的。实际上,你说得并不对,req.url 其实就只有 path,并不是拼好完整的 url 。

另外,迷弟太烦人了,这是我最后一次回这个话题。你完全不理解多调用一个这么大的函数意味着什么,对于 CPU 指令缓存和乱序执行管线有什么影响,尽扯一堆没用的。事实上,我说的是不公平,并没有说这样会比字符串比较慢哦。

@pinxue 代码一样测试才公平,那所有测试都不公平了。

另外我不用fibjs,也不用node,我不是帮谁,而是实事求是。

你到现在都没明白自己问题在哪里,你自己提供的代码到底执行几行你搞明白没?响马cpp里做了和node一样的事情,你明白没?你是不是认为cpp代码欺负了JS代码?叫你测试你又不测试,自己给的代码都没整明白,就敢出来质疑别人,node社区就是被你们这些自以为是的带沟里去的。

再次嘲笑node社区的一些人,前端不强,后端不行。

回到顶部