背景
- 解决的问题:解决web端高并发问题,例如i/o密集操作
- 什么是I/O密集:指的是文件操作、网络操作(频繁埋点->网络操作)、数据库等等
- 什么是CPU密集:指的是逻辑处理运算、压缩、解压、加密、解密等等
- 为什么:NodeJS处理并发的能力强,但处理计算和逻辑的能力反而很弱
Node.js
- Node.js是建立在Google V8 JavaScript引擎之上的网络服务器框架,允许开发者能够用客户端使用的语言JavaScript在服务器端编码。
- JS的执行机制:JS设计是单线程的,通过事件环Event Loop实现异步开发。
- Node.js正因为node的Event Loop原理,使NodeJS处理并发的能力强。
Node工作流程:
- V8引擎解析JavaScript脚本,若解析后的代码,调用了Node API,Node就会将代码交给libuv库处理,这个libuv库是c语言写的,也就是node的事件环的核心。
- libuv库负责Node API的执行,它将不同的任务分配给不同的工作线程(work threads),通过多线程同步阻塞执行,模拟异步处理机制,成功后将回调函数放入Event Queue。
- 等到work threads队列中有执行完成的事件,就会通过execute callback回调给Event queue队列,把它放入队列中。
- 最后通过事件驱动(发布订阅)的方式,取出EVENT QUEUE队列的事件,再通过V8引擎将结果返回给应用
- 传统的server 每个请求生成一个线程,nodejs是一个单线程的,使用libuv库保持数万并发
- libuv:c语言编写的基础库实现主循环
- NodeJS它的所有I/O、网络通信等比较耗时的任务,都可以交给worker threads执行再回调,所以很快
- 非阻塞事件驱动 实现异步开发,通过事件驱动的I/O来操作完成平台数据密集型实时应用
Node事件环
- node中的event loop是在libuv里面的,libuv里面有个事件环机制,它会在启动node时,初始化事件环。详见 juejin.cn/post/684490…
与传统webServer对比
- 传统多线程
- node事件处理
- 脱离带宽内存与计算量来讨论并发是没有意义的。
- 在内存受限的情况下,node.js就有优势了。
- 传统server,假设一个进程需要1M内存,为了能同时开1000进程,你需要额外的1G内存来给它。
- 而对于node.js,它可能只需要20M来完成这个事,代价就是每个客户端都可能需要多等那么一小会儿。
- node.js的优势严格来说不是并发而是“非阻塞”
总结
我们所看到的node.js单线程只是一个js主线程,本质上的异步操作还是由线程池完成的,node将所有的阻塞操作都交给了内部的线程池去实现,本身只负责不断的往返调度,并没有进行真正的I/O操作,从而实现异步非阻塞I/O,这便是node单线程和事件驱动的精髓之处。
控制并发
- 用eventproxy、async.mapLimit、async.queue等控制并发
- 以eventproxy为例:分批处理任务
var EventProxy = require('eventproxy');
const most = 5;//并发数5
var urllist = [....];//待抓取url列表,100个
function foo(start){
var ep = new EventProxy();
ep.after('ok',most,function(){
foo(start+most);//一个批次任务完成,递归进行下一批任务
});
var q=0;
for(var i=start;i<urllist.length;i++){
if(q>=most){
break;//最多添加most个任务
}
http.get(urllist[i],function(res){
//....
res.on('end',function(){
ep.emit('ok');//一个任务完成,触发一次ok事件
});
});
q++;
}
}
foo(0);
async.mapLimit和async.queue实现方式见 blog.csdn.net/qq_42306443…
其他措施
- 举例天猫:node服务器的两个角色,解决高并发问题
- 一个处理所有的来自客户端的https请求
- 另一个就是为传统的服务器分发http请求
- 增加机器分布(内存/带宽/计算力等),均衡压力
- 其他技术方案:php+Openresty java-Netty等