少废话,上代码。nodejs 代码如下:
var http = require("http");
var server = http.createServer(function(request, response) {
response.writeHead(200, {
"Content-Type": "text/html"
});
response.write("Hello World!");
response.end();
});
server.listen(8000);
测试结果:
lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8000/
Running 30s test @ http://127.0.0.1:8000/
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 38.61ms 6.18ms 77.46ms 84.50 %
Req/Sec 837.87 134.82 1.77k 81.78 %
297965 requests in 30.00s, 44.05MB read
Socket errors: connect 0, read 453, write 0, timeout 90
Requests/sec: 9931.42
Transfer/sec: 1.47MB
fibjs 相似代码如下:
var http = require("http");
var svr = new http.Server(8080, function(req) {
var rep = req.response;
rep.addHeader({
"Content-Type": "text/html"
});
rep.body.write(new Buffer("Hello World!"));
});
svr.run();
测试结果如下:
lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/
\Running 30s test @ http://127.0.0.1:8080/
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 16.73ms 31.61ms 162.45ms 94.93 %
Req/Sec 3.25k 822.21 5.28k 89.45 %
1137450 requests in 30.00s, 160.54MB read
Socket errors: connect 0, read 282, write 0, timeout 0
Requests/sec: 37916.03
Transfer/sec: 5.35MB
可是有些代码对 fibjs 纯属多余,精简一下:
var http = require("http");
var svr = new http.Server(8080, function(req) {
req.response.body.write(new Buffer("Hello World!"));
});
svr.run();
测试结果如下:
lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/
Running 30s test @ http://127.0.0.1:8080/
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 24.39ms 71.85ms 343.96ms 95.11 %
Req/Sec 3.99k 1.00k 6.67k 91.61 %
1379337 requests in 30.00s, 161.80MB read
Socket errors: connect 0, read 270, write 0, timeout 0
Requests/sec: 45980.00
Transfer/sec: 5.39MB
写的一个测试用例,你们折腾去吧!
/**
* Don't use `ab`. `ab` sends HTTP/1.0 in HTTP requests. This causes node to
* close the connection after each request by sending a `Connection:close` in the response.
* Adding `-k` for keep-alive to ab does not help.
*
* If you use `siege`, make sure to set connection = keep-alive in your .siegerc file.
* If not, siege defaults to HTTP/1.0.
*
* @author liveinjs
* @version 0.0.1
*/
var http = require('http');
http.globalAgent.maxSockets = Infinity;
var numCPUs = require('os').cpus().length;
var cluster = require('cluster');
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
http.createServer(function(req, res) {
res.end("hello world!");
}).listen(8080);
}
@ravenwang 看了下介绍, fibjs 是 v8单线程 + 后台多线程,后台线程里做了什么优化是否充分利用多核、内存,我还没看。 不过,node.js 平台 所有的纯js 库,只要你真的需要,你都可以完全通过 编写 C/C++ Addon ,重写个C/C++ 版的。 能把服务器压榨到什么程度,就看你的C/C++水平了,自由发挥! 不过,fibjs 也得支持,关注中。
又撸了撸FibJS的文档,作者最得意的应该是coroutine相比回调的简洁优雅(且不提generator),于是我去看coroutine,发现这货根本不是coroutine,而是个Fiber管理器,至于Fiber,是个更轻量的线程,而且我在coroutine接口里看到了恐怖的东西:Lock
和Semaphore
,有了这俩玩意还让不让人愉快地写异步代码了…
我底层懂得少,如果讲了什么小白的话请往死里批
@ngot 唔对不起,我表达错误,这里不讨论速度,说的是fibjs用所谓的coroutine解决异步的问题,从代码编写和理解的难度来说,同步<异步<<多线程,fibjs是希望用同步的方式写异步代码来降低理解难度,但方案却是把多线程那套东西给引进来了
如果我理解正确的话,Fiber是并行的,这会产生共享资源占用的问题,所以引入了Lock
和Semaphore
来解决这个问题,我不清楚fibjs中类似于两个Fiber共用同一闭包时的代码要怎么写,如果连这个都要用Lock解决的话,我只能呵呵了
居然给转这边了,统一回复一下吧。
这并不是一次严肃的对比测试,只是一时兴起测着玩的,不必认真。简单说结果吧,孢子社区已经在 fibjs 上开发了数万行代码的服务器应用,并且稳定运行一年多。
有同学看见锁就惊恐,是不对的。nodejs 在需要资源受限时同样需要锁,并且异步锁的实现令人作呕。协程的好处是你不需要对内存加锁,但是不代表你不需要对资源加锁。比如限定数据库连接数,阻止缓存重复更新,等等,很多高级场景都是需要锁来保障的。
同时,nodejs 从来就不是单线程,同样是单线程 v8 + 后台多线程,与 fibjs 相同,并且 nodejs 的 http parse 也是 c 写的。至于说 nodejs 也可以通过 addon 来提升性能,那就强词夺理了哈哈。
最后用我在 fibjs 论坛里的一段回复结束吧:
高并发不等于高性能,随着应用层逻辑的引入,会拉平甚至拉低不同平台的并发性能,越复杂的应用,对语言本身的运算速度依赖越大。
因此拿简单的 echo 来对比并发其实是没有意义的,不同语言,开发环境,最后实际上瓶颈根本都不在基础架构上,大部分应用最后每秒能响应 100 个请求便算上很好了,基础架构每秒 5000 还是 50000 对此影响很小。
所以结论是应用开发框架比并发性能是一件很蛋疼的事情,开发范式的改变带来的应用可读性,模块低耦合,容易工程化所带来的性能提升反而收益更高。
何况 fibjs 就连基础的并发性能也秒杀 nodejs 哈哈。
@xicilion 的确是高精尖的东西, 需要支持. 相比node.js的优点在哪: 五倍速度? 同步方式写异步? 如果只是这两点fibjs还没发撼动Node.js生态, 如果速度有几十倍的差距则另当别论
@xicilion 我用Node时间不长,还没遇到过js代码需要加锁的情况,能举个例子说明下令人作呕的异步锁吗? 前面有同学提到改用C/C++重写addon怎么能算强词夺理呢,fibjs目前的速度优势主要应该在于把Node中部分用js实现的库用C/C++多线程实现了吧,前面也提了要比速度的话比较公平的方案应该是大家都把多核跑满来比 你也说了基础架构上5倍的速度根本不值一提(是不是真能有5倍的优势另说),我对fibjs感兴趣的也是用同步方式处理异步代码的能力,目前看到的也仅仅是提供了coroutine,相比Node加个coroutine的包相比也没啥优势,实在没看到Node转fibjs的任何理由
@ravenwang 例子我说了,当你需要限制数据库连接的时候,或者缓存失效,需要更新缓存,但是多个请求同时请求,导致多次不必要的更新请求。
你说的都是真理,如果你把 fibjs 做的事情按照 fibjs 的方式在 nodejs 上做一遍,当然没有迁移到 fibjs 的理由。你赢了哈哈。
不过前提是你先写 4万行 c++ 代码。
@ravenwang 另外,fibjs 不是仅仅提供了 coroutine 这么简单。fibjs 实现了一整套同步 io 模块,网络模块,数据库接口等等,是一套完整的开发框架。
如果只是 v8 + coroutine,两百行就写出来了。你把 node 挂上 coroutine 包,面对一大堆不能用 coroutine 的各种 io, net, db 模块,你能干什么?
@xicilion 我没什么要赢的这也没什么输赢可讲,你能做出fibjs并且开源我非常佩服,既然贵司员工来这里推广(在我看来推广方式并不怎么友好),于是我就站在一个小白用户的角度看一看,提出我自己的疑虑而已,我也没有挺Node的意思,只要fibjs给我足够的理由我会毫不犹豫地迁移过去
目前来说,从我所看到的fibjs的文档以及样例代码,基于我所能理解的知识范围,fibjs所提供的coroutine相比Node所能支持的其它异步方案比如promise、generator、甚至是coroutine等在编程范式上并没有多大的优势,而性能上也没有压倒性的优势,也许一个更完整的demo对比才能体现出fibjs的强大,或者说能让我这样的小白理解fibjs的强大
祝fibjs能获得更多关注和支持
关于Callbacks , Coroutines和 Generators。看看TJ如何评论。[https://medium.com/code-adventures/callbacks-vs-coroutines-174f1fe66127]
同步在异步面前,等于浪费资源,等待,等待,再等待…
CPU是有限的,金钱也是有限的,这就是现在NGINX大胜APACHE的原因。 连SCALA和GO都在探索异步驱动编程。 而我认为你的这个方式反而是一种倒退。
你压根就不懂nodejs,所以不要用你的这些东西来对比了,没有意义。 你甚至连异步是什么都不理解。
你所谓的这个东西,用scala、go写的能甩几十条街。
而且,有一点,如果你想对比测试,就测试文件IO,数据库TCP通信,
测试CPU输出有什么样的参考意义?res.write()
这种代码毫无意义。
要测试就测试fs.readFile()
, fs.writeFile()
, 以及数据库TCP.
@ngot TJ文中所讲的Coroutine是指Full Coroutine,目前FibJS在JS层面所提供的Fiber是没有那个能力的
说道这我好像突然明白FibJS的理念了,是把底层Coroutine化,JS接口部分改成形式上同步实质上却是异步执行的,这个确实牛逼,我之前只是在看JS部分所提供的东西,所以一直没理解,也就是说,在FibJS中:
var f = fs.open('test.txt');
f.write('something...');
f.close();
类似于上面这样的代码,每一步都是异步执行的,这样的确可以称得上是在范式层面的巨大进步
@tulayang fibjs 和 go 的模式是一样的,后台全部异步,前台面对开发者,将异步映射为同步协程。
协程方式上,fibjs 也是采用与 go 同样的模式,不是使用 gen 伪造协程,而是通过栈切换完整保存现场。这样的好处是在 javascript 层完全不必考虑实现方式,只需要按照最直接的思维方式写出业务逻辑即可。
[@ravenwang](/user/ravenwang) 说 fibjs 比 node 异步更狠还是蛮准确的,fibjs 的工作线程是全部由 c++ 完成的异步逻辑。换句话说 Node 是在用户线程异步,fibjs 则是把异步延伸到后台大量工作线程。
不过 [@ravenwang](/user/ravenwang) 说同步只是写代码方式这一点不准确,如前所述,fibjs 不是语法上的技巧,而是硬碰硬地切换堆栈。所以不是写代码方式不同,而是 javascript 代码实实在在就是同步的。
nodejs的异步逻辑也是c++完成的,js本来就是解释性的语言。
v8是c++的,js的事件驱动过程本来就是c++完成的。
nodejs的整个事件驱动完全是交给v8,v8有多快,他就有多快。
js的性能瓶颈仅限于js代码的循环,和数字、字符向c++的转换。
这是不一样的。
多进程非异步并发,跟多进程异步并发,有本质的区别。
比如2个CPU,同一时间都可以参与2个事务。 但是异步,在接下来的时间则可以继续参与其他事物,非异步则只能等待。
他们都是多进程并发。
@tulayang bug 没搞掂,回来说两句。
首先,fibjs 不是语法糖,跟你见过的其他各种 js 伪同步的东西不一样。另外,并发不论是多进程还是多线程,都是在操作系统协调下的后台异步前台同步,你的文件读写,控制台输入输出,甚至同步网络操作,都不会出现你说的等待的时候 cpu 等在那里的情况。因为操作系统在发现同步线程需要等待的时候,便保存现场,把 cpu 分配给别的线程了,等底层异步完成后,再触发事件唤醒等待的线程或进程。
fibjs 采用的是用户空间线程技术,windows 上也叫纤程,这个其实是很古老的技术,远在 Linux pthread 出现以前,就有一个叫 pth 的项目 帮助 Linux 程序员实现纤程。这种用户空间线程不同于操作系统线程,是非抢先的,必须依赖于各个纤程主动交出控制权,其他纤程才可以被唤醒。
pthead 出现之后,这种技术慢慢被人淡忘,直到大家越来越发现在应用层处理多线程带来的运行成本和程序员不熟练带来的风险。
异步处理便是一种尝试,不过也并不是什么新技术,哪怕最古老的操作系统,内部实现也都是异步的。unix select 开始把这种范式带到应用层,windows gui 的消息处理也是异步编程,其它的比如 Windows 的完成函数,iocp,linux 后来赶上的 epoll,freebsd 的 equeue,都是运用了十几甚至几十年的技术了。
然而毕竟异步是非人类的。于是越来越多的架构开始努力不那么异步,nodejs 社区便做了很多努力。而目前为止,最让人称道的恐怕算是 go 的 goroutine 了,goroutine 便是典型的用纤程来实现用户层同步,基础库异步的方案。而 javascript 以纤程为基础提供完整解决方案的 唯 fibjs 一家,nodejs 虽说也有第三方 coroutine 模块,但是只实现了最基础的纤程处理,而整个 nodejs 生态都是建立在异步基础上的,导致 coroutine 根本不能发挥,因此几乎一发布便被遗弃了。
用户空间线程因为是非抢先的,意味着不会出现多个纤程同时修改和访问内存,不会出现多线程开发的绝大多数风险,但是缺点是因为所有纤程其实共享一个物理线程,所以也会出现 nodejs 一样的运算瓶颈。
针对这个情况,fibjs 的策略是只在极少数必须的运算采用纤程,而所有可能需要大量运算,或者需要等待的 io,都投射到后台工作线程,或者异步完成,尽可能把宝贵的 javascript 线程的运行时间留给业务逻辑,从而最大化 cpu 利用率。
这样做的代价便是 fibjs 的基础库很难像 nodejs 那样大量使用 javascript 实现,只能用 c++ 完成几乎全部基础库。
@ravenwang 我有疑问。
就你举的这个例子,是否可以理解为:这三句JavaScript代码是阻塞执行的,而它们调用的底层C++模块是异步执行的?
如果是这样,那显然跟Node.js有差别。试比较:
var fs = require('fs');
fs.open('/some/file', 'w', function(err, fd) {
// write data to the file
fs.write(fd, buffer, 0, 100, function(err, written, buffer) {
fs.close(fd, function() {
});
});
});
var total = 0;
// more processing
上面的代码,调用的文件处理模块当然是异步的。而JS主线程是非阻塞的,也就是会直接继续执行var total=0
这一句。如果换成fibjs,是否意味着主线程要等待文件操作执行完,才执行这一句?
js伪同步? 我是认为你的fibjs c++层面使用了语法糖,用结构来缓存处理程序而已。
操作系统的程序当然不需要等待,尤其是linux unix这种。但是服务器程序,却会等待。
服务器程序要跟操作系统交互,就要等待操作系统的返回值。如果你不是异步,那你的服务器程序必然要等待,这就要阻塞。你所说的不等待,只不过是cpu的记忆、切换而已,但是处理程序却是在等待的。
如果程序只能控制两个cpu,那么非异步,只要任何一个没有完成任务,不论你cpu再怎么切换,业务程序都是处于等待状态。他要消耗两个内存单位,占用两个cpu处理单位,任何的后续业务都会被阻塞。
至于线程,nodejs是不使用的。
我觉得,nodejs作者跟nginx作者一样,在设计的时候,只选择了类Unix系统,多进程操作系统。windows在多进程上的初始化开销和速度,使他并不适合这种方式。至于后来有了windows版本,那也不过是微软的努力而已。
而类Unix系统,其目标就是多进程,而非多线程,这使得他所建立的任务安全可靠、易调试,没有副作用,他所建立多进程的开销比windows小的多得多。
我不认为nodejs使用多线程有什么好处,这会引起大量的副作用,他的职责应该是消息通信,而不是复杂计算。做顶点计算,随机序列,不是他的目标。当然,有人希望nodejs做得更多,我却认为这是画蛇添足,把nodejs往java的老路上推。
如果需要nodejs做得更多,我觉得更好的方式是: nodejs <----------------------> 操作系统 <------------------------> c c++ java …业务
既没有同步等待,也没有副作用,而且由c c++ java …负责计算,nodejs只是负责告诉操作系统,我需要什么。 现有的例子莫过于c++写的数据库业务了,而这个数据库根本不需要用nodejs去写。 而这个业务也同样可以是顶点计算,序列…
nodejs就好比是列车,而复杂计算则是货物,从没有人期待nodejs即做列车又自备货物。
@bnuhero 同样效果fibjs要这么处理
var fs = require('fs')
coroutine.start(function() {
fd = fs.open('/some/file', 'w')
fs.write(fd, buffer, 0, 100)
fs.close(fd)
}
var total = 0
调用fs.open()
时看起来当前正在执行的代码是阻塞了,但v8也就是JS线程是不阻塞的,fs.open()
之后,控制权就转给了底层协程引擎,协程引擎会调度其它Fiber来执行,比如处理多个请求时每个请求对应一个Fiber,这时就可以处理下一个请求了
这里的关键在于上面的代码完全是传统的同步写法,却同样享受到了Node异步的好处
所以,在fibjs中,执行JS代码的 --> 是支持用户态多纤程的单线程V8引擎实例,是这么理解的吗?
因此,所有的核心模块(包括fs
在内)都得用C++重写以支持纤程的工作方式。
另外,是否与JXcore做过对比,谁的处理速度更快些?JXcore有一个好处是,100%与Node.js兼容。
@tulayang 建议你了解一下coroutine/fiber和线程的区别,fibjs里的等待底层返回和node里等待底层调用回调函数是一样的,这时v8是不阻塞的,可以继续处理其它fiber,简单来说fibjs只是把回调函数换了一种写法而已
@bnuhero 是的,而且我觉得因为基本的设计哲学不同,fibjs应该几乎没法利用node的现有成果 fibjs的优点不是速度(当然这是相对于node来说),它跟node是站在同一个基础之上的,从理论上来说即使快也快不了多少,fibjs解决的是node/javascript一直在试图解决的异步编程模式问题
你这些所谓的“同步代码”,不过是把语法糖从js移到了c++,光这一层面没有实质意义。
用栈或者其他压入还未初始化完成的逻辑,等待完成再出栈运行。
把js代码移到c++,这没有任何意义,而且是画蛇添足。
单从OO上,js足够处理这种组合模式。
从FP上,js简直就是小儿科。
不要忘了,js拥有c c++所不拥有的lisp FP特性,js被发明出来本就是简化这种任务,而现在你却反过来在c++里写。
4万行代码,我看充其量也就js 4k行的功能。
@bnuhero fibjs 和 JXCore 解决的问题是不同的。JXCore 试图解决 nodejs 单线程瓶颈,他的实现方式比较简单,是在进程中开启多个 v8 内核,各自在自己的空间和线程内工作,好处是可以充分利用多核(类似 cluster 的单进程版),缺点是每个 v8 内核是独立的,内核之间传递变量,函数,共享,都必须经过处理,简单来说就是进程内的 rpc。
Yes, there have been discussions. I think, I won’t be adding coroutines. I don’t like the added machinery and I feel that if I add them, then people will actually use them, and then they’ll benchmark their programs and think that node is slow and/or hogging memory. I’d rather force people into designing their programs efficiently with callbacks. ----- Ryan dahl
@ravenwang fibjs 因为 异步-> 同步转换的原因,其实很多地方性能是低于 nodejs 的,但是实测起来,性能却高于 nodejs,根本原因,还是编程范式的改善,极大简化了业务逻辑,带来的性能提升。
同时 fibjs 还会转化工作方式,在只发现一个纤程时,不需要使用后台线程,则自动选择纯同步模式,以避免不必要的任务切换负载。
比如同样的 leveldb 封装,多纤程下,fibjs 性能是 nodejs 的三倍,单纤程下,则是 nodejs 的接近 9 倍。(这个话题拜托别展开,我只是发现封装后性能比 leveldb 官方声称低一个数量级,才找来 nodejs 的封装测一下的,同样不是认真的性能对比测试,不必认真)
这一点在业务逻辑层尤为重要,简化的编程范式带来的性能提升,反而远大于核心库优化带来的性能提升。毕竟绝大多数应用的瓶颈,其实都是复杂的业务逻辑,而非核心库本身。
看了楼上各种大大的言论,吓得我都不敢说话了。
首先撸主的确很流弊哈,只不过目前最大的软肋就是生态圈抵不过——除非撸主去把 npm 的包给搞到自己的镜像然后宣称兼容。
以及——其实在 0.11 的 node 版本一下,不知道大家有没有用过 fiber
这个 C++ 的 addon。
@XadillaX 这个问题我在上面说过:
javascript 以纤程为基础提供完整解决方案的 唯 fibjs 一家,nodejs 虽说也有第三方 coroutine 模块,但是只实现了最基础的纤程处理,而整个 nodejs 生态都是建立在异步基础上的,导致 coroutine 根本不能发挥,因此几乎一发布便被遗弃了。
同时,fibjs 生态的确比不过 nodejs,这一点必须承认,哑口无言哈哈。
@tulayang 感觉你越走越远了… 不但nodejs本身是js线程+后台多线程的,你在nodejs中的js代码,只要你乐意,也可以创建进程(child_process)和线程(webworker-thread),至于是基于事件还是基于同步原语,你完全不必拘泥于单一的编程范式。
纤程之于线程,类似于线程之于进程,你把fibjs里面的js线程想象为一个进程,把纤程想象为用户态的、非抢占式线程,也许很多东西你也就可以想通了。你拥有了在单一的线程时间片中自我调度执行序列的能力,并可以安排给不同的任务去伪并行执行,这与**“用栈或者其他压入还未初始化完成的逻辑,等待完成再出栈运行”**完全不搭界的啊。
对于习惯异步方式还是同步方式来编码,完全是个人选择问题,但如果走进了用callback搞定一切的单一思维方式,那也就完全限定了自己的能力。
同样的,“不要忘了,js拥有c c++所不拥有的lisp FP特性,js被发明出来本就是简化这种任务”,这话也没一点道理,且不论js的历史,大家都知道;C++对函数式编程范式并不是完全没有支持,C函数指针不说了,模板元编程是有的,lambda也是有的,(嗯,还有操作符重载,甚至#define Macro),高阶函数和偏函数都提供了优雅的支持。
代码都是人写的,多了一种工具也就多了一个可以玩的东西,也多了很多的可能性,你还是你,又没有改变。Coding的世界这么大,没必要在了解之前就去排斥吧。
@tulayang 你确定?
$ ps
PID TTY TIME CMD
50804 pts/3 00:00:01 bash
58260 pts/3 00:00:00 node
59199 pts/3 00:00:00 ps
$ ps -L 58260
PID LWP TTY STAT TIME COMMAND
58260 58260 pts/3 Sl 0:00 node --harmony c.js
58260 58261 pts/3 Sl 0:00 node --harmony c.js
58260 58262 pts/3 Sl 0:00 node --harmony c.js
58260 58263 pts/3 Sl 0:00 node --harmony c.js
58260 58264 pts/3 Sl 0:00 node --harmony c.js
58260 58265 pts/3 Sl 0:00 node --harmony c.js
$ ls /proc/58260/task/
58260 58261 58262 58263 58264 58265
$ find /proc/58260/task/ -name stat -exec cat {} \;
58260 (node) S 50804 58260 50804 34819 59202 4202496 5157 0 41 0 38 18 0 0 20 0 6 0 51725428 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140734762386728 140245676173961 0 0 4096 134234626 0 0 0 17 0 0 0 16 0 0
58261 (OptimizingCompi) S 50804 58260 50804 34819 59202 4202560 93 0 2 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245675281904 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58262 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695659600 140245679055904 0 0 4096 134234626 0 0 0 -1 1 0 0 0 0 0
58263 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695532624 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58264 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695462992 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58265 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695393360 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
$
@zalemwoo 哈哈,完整当然是相对的,这里指的是开箱即用,是有完整的多任务调度和并发控制,完整的并发文件 io 和 网络 io,一套完备的 http 客户端服务器端解决方案,ssl 支持,以及数据库支持。
对于绝大多数场景,fibjs 的基础库已经足够应付了。
@tulayang 看来的确不了解,建议学习一下 goroutine,熟悉 c 的话学习一下 linux 的 getcontext, makecontext 和 swapcontext,熟悉 windows 的话了解一下 Fibers。
fibjs 为了保证可移植性,连这些都没用,是自己实现的 fiber。但是原理都是一样的。
再说一句,windows 的 fiber 的历史有快 20 年了,Linux 的 context 历史可能也有十年了。我前面提到的 pth 是在 linux 支持线程之前便有的方案,但是实在太久远,不建议了解了。
另外,“c++对函数式编程范式并不是完全没有支持”,这不是很可笑的吗?
一个语言,就算你添加了lambda也不代表你运作的了函数式。 c++已经是一个很可笑的语言了,添加了函数式的c++就是更可笑了。
如果这世界只剩下c和c++,我绝对不会选择c++。
我对windows没有任何兴趣,对于windows思想搭建起来的服务器更没有兴趣。
对于服务器来讲,我觉得线程是多余的,对于图形编程,线程也许是必须的。
副作用、记忆带来的内存CPU损耗,这都是服务器所不喜欢的。
function func() {
}
func.start();
便会创建一个纤程,将 func 作为入口,并加入调度列表,当有纤程出让控制权时,此函数便会被执行。切换方式是保存全部 cpu 寄存器,加载目标纤程的 cpu 寄存器,恢复 ip 和 sp。这是标准的纤程处理方式。
jacksontian node $ ./tools/wrk/wrk http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 4.19ms 7.15ms 54.22ms 93.58%
Req/Sec 3.91k 3.82k 16.33k 81.44%
70239 requests in 10.01s, 9.91MB read
Requests/sec: 7016.96
Transfer/sec: 0.99MB
jacksontian node $ ./tools/wrk/wrk http://127.0.0.1:8000/
Running 10s test @ http://127.0.0.1:8000/
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 840.83us 358.15us 5.87ms 85.23%
Req/Sec 6.41k 1.21k 9.70k 64.59%
121425 requests in 10.00s, 17.95MB read
Requests/sec: 12143.27
Transfer/sec: 1.80MB
jacksontian node $ ./tools/wrk/wrk -t12 -c400 http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 18.40ms 24.78ms 131.81ms 95.24%
Req/Sec 1.55k 0.95k 5.96k 63.82%
169208 requests in 10.01s, 23.88MB read
Socket errors: connect 155, read 235, write 0, timeout 653
Requests/sec: 16907.91
Transfer/sec: 2.39MB
jacksontian node $ ./tools/wrk/wrk -t12 -c400 http://127.0.0.1:8000/
Running 10s test @ http://127.0.0.1:8000/
12 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 24.90ms 2.52ms 46.70ms 64.25%
Req/Sec 817.54 397.28 1.77k 70.75%
96824 requests in 10.00s, 14.31MB read
Socket errors: connect 155, read 61, write 0, timeout 620
Requests/sec: 9679.52
Transfer/sec: 1.43MB
以上 8000是node,8080是fibjs。看起来跟连接数有些关系。
连接数少的时候,node性能还略好些呢。
连接数多的时候,差距没有5倍那么大。
以上测试仅供参考。
说句公道话, 前端js写过好多, nodejs也玩过, ES6生成器也折腾过. 异步回调看着优美, 实际用于生产环境真的是开发代价太高. 容易出问题, 调试麻烦. 生成器特性一度让我看到希望, 但是真正折腾进去后, 完全就是一个地狱来到另外一个地狱, 写法特别恶心人. 后面发现了fibjs, 同步的思想来开发, 的确是可以提高很多的开发效率和减少错误的发生. 一些异步产生的错误是偶发性的, 先不说重现的难度, 就算你知道问题, 你要去解决这个异步执行的流程也是非常曲折.
fibjs的确是支持不足, 基本api功能还有欠缺(其实是我的使用比较特殊, 业务性的api接口比node要丰富, 起码是所有数据库模块都已经有了)
经过研究的结果就是, 把已经写好的node底层框架全部从写成fibjs实现. 现在是node+fibjs, websocket还没有第三方库支持, 暂时只能用node的socket.io
@xicilion 比較一下幾種寫法:
nodejs with callback
var fs = require('fs');
fs.readFile('in.txt', 'utf-8', function (err, txt) {
if (err) { throw err; }
console.log(txt);
});
fibjs
var fs = require('fs');
coroutine.start(function() {
var txt = fs.readFile('in.txt', 'utf-8');
console.log(txt);
});
nodejs with co & generator
var fs = require('fs');
var asyn_readFile = thunkify(fs.readFile);
co(function * () {
var txt = yield async_readFile('in.txt', 'utf-8');
console.log(txt);
}); // return a promise
nodejs with es7 (in future)
var fs = require('fs');
var async_readFile = asyn function(file, opts) { .... };
var txt = await async_readFile('in.txt', 'utf-8');
console.log(txt);
其中第三種就是我目前常用的寫法…… 通過 thunkify 這個工具(用處:fn(arg1,arg2,…, cb),等價于 thunkify(fn(arg1,arg2,…))(cb) 僅就避免回調地獄這一件事來説,已經達到和 fibjs 和未來的 es7 同樣的結構了; 而批量的 thunkify 以及 forEach 也很容易用簡單的工具方法實現。 它的優點是不言而喻的——兼容一切現有標準 callback 函數。
在這種情況下,換用 fibjs 的意義還有多少?
p.s. 真心請教,沒有隱含“沒意義”這種答案的意思……
@xicilion 又看了遍帖子 ,突然想到,fibjs可否提供兼容nodejs的全套API,这样就可以利用node的生态了。 自己的同步API可以比如加fib前缀来调用。 这样可以即利用node的生态,也可以使用fib的同步开发模式来开发。
@wuliao49 翻来覆去考虑很久,最后决定不去兼容 nodejs 的 api。
一、完全兼容工作量太大,还要支持丑陋的回调,太让人反胃。 二、还有很多事情要做,从我的项目而言,计划中的模块比兼容 nodejs 价值要大得多。 三、node 巨大部分模块稍作修改便可使用,没有大家想象中的那么不兼容。
题主需要冷静一下,回答全部问题都抱着“回调恶心反胃这样的先入为主基调”,这样讨论就没意义了。 解决 “回调地狱” 是一个问题,但不能因此就说回调丑陋,我反而觉得回调是一种非常棒的体验。 都说异步反人类,其实只是反了你们惯有的编程逻辑罢了,生活中的事物大部分不都是异步的吗? 所以说异步才是真正迎合人类,迎合更多的现实场景。 以往的全同步思维才是反人类,是机器思维。 言毕,仅此而已,有点跑题,并不是讨论fibjs和node
@captainblue2013 对于有争议问题的讨论,其实就是表达自己的观点,让对方接受。在讨论过程中,肯定要始终坚持贯彻自己的观点。如果自己都不坚持,怎么让别人接受呢。我所有的讨论其实就是为了,让大家接受回调丑陋这个观点。当然,你可以反对。
@captainblue2013 同步或者异步,只是事务的运行方式,并不是事务的逻辑,所有事务的逻辑都是以同步方式描述的。异步编程,实际上是把同步描述好的业务逻辑投射到事件处理函数内。不用讨论,这确定是反人类思维方式的。