服务器
当我们从一个前端或者后端走向全栈的时候,都会遇到一个问题:我人生中的第一台云主机(服务器)应该买什么配置的。
是不是cpu核心越多,内存越大,硬盘容量越高越好??
相信大多数同学都有这个疑惑,一定是越贵的主机,性能就越好吗?理论上来说,是这样的,但是这并不代表你就应该多花那些冤枉钱—因为nodejs是单线程的,你想一个进程,一个线程的程序(就算它到了i/o环节最多也就是4个一组去执行)它对资源的消耗是很小的。如果你的项目比较简单,简单到根本不需要调用第二个进程去干这件事儿,那么单核cpu就够了。
我什么时候需要多线程
当你的项目非常庞大的时候,庞大到什么程度,举个例子,比如用户登录和用户在系统中修改某个数据。
这两个操作,没有任何直接关联,甚至他们发生的时间段都不同,比如用户,10点登录了,12点修改了某个数据。此时,这两个操作没有直接关系,但是,这两个操作都需要消耗很多的系统资源—比如几十万个用户在频繁的登录..此时,就有必要给这两个任务区分进程去执行了!
分开两个进程可以有效的增加系统资源利用率。后面我们会讲这是为什么。
这里你只需要了解,一个cpu,一次只能处理一个进程,但是如果有两个cpu,他可以同时处理两个进程,分进程就好处就体现出来了。那么,什么是线程,什么是进程呢?
什么是线程,什么是进程
说实话,看到这个东西,绝大多数人是懵逼的,就连我认识的一些从业几年的同事,一时半会也解释不清。
今天龙哥将会用最简单的几句话为你讲明白:首先,在讲这两个东西之前,我们要了解电脑是怎么运作的...
杠精马上反问:线程跟进程,你扯什么计算机原理啊?
别急,因为线程和进程就是基于这个来的。
我们都知道,组成一台计算机(这里指主机,不包括显示器,键盘等外设)。最核心的就是这几个东西,cpu,内存,硬盘,主板。
那么,你知道它的工作流程是什么样的吗?其实你会发现,这里面除了cpu是用来计算数据的,其他的东西都只干一件事,那就是储存。
无论是哪个硬件发送来一条指令,这些指令都会形成一个队列,然后一个接一个发送都cpu那里去处理,放心,cpu的执行效率高的惊人,惊人到一个一个处理,会让人误以为是并行完成的。
而这个调用cpu转一圈出来的流程,人们称之为:流。
然后你该说了, 这跟进程和线程有什么关系??
所谓的进程,就是一个运行中的程序,比如,我们开了一个QQ,打开任务管理器,你看到这个QQ就是一个进程。
注意,这里就很关键了,我们前面说,cpu一次只能处理一个指令,而这个指令就是进程。
然后,当这个进程不活动的时候,它的数据就被储存在内存中,内存也叫这个程序的暂存器。
好,先别蒙,那我们简单来说,一个程序就是一个进程。那nodejs就似乎一个进程了呗?对,完全正确,我们运行的环境,包括mysql,redis这些程序都是独自占一个进程。
行,那么线程呢,老有人说,多线程,多线程有什么用??
单线程和多线程
首先我得告诉你,nodejs是单线程的。所谓线程,就是在一个程序(进程)中,任务是怎么处理的。
如果是多线程,就是多个任务一起执行,单线程,就是一个一个执行。
但是,前面我们说了。最后处理这个任务的,并不是内存,而是cpu啊。cpu一次只能执行一个任务。所以多个进程,多个线程,只是看上去是并行的,其实最后还是一个一个执行。
所以,多个核cpu就是为了解决这个问题而存在的。。理论上来说,你的硬件越牛逼,cpu核数越多,你调用多线程或者多进程的收益也就越大。但是,花的钱也就越多。。
区别
那照你这么说,他俩就没什么区别了呗。有区别,进程里面是包含线程的,所以,当你需要多个cpu同时处理的时候,需要建立多个进程,当需要一个进程里面的任务,分别运行的时候,需要多个进程。
好我讲完了。那么你发现问题了吗?
nodejs是单线程的,我说了两遍了。如果是这样,那么它岂不是在cpu使用上,不如java等可以多进程的语言了吗??
nodejs
还真不是,nodejs只是看上去是单线程的。因为js是浏览器语言所以它必须是单线程。但是,真到了执行的步骤,你可管不了它怎么运作。
引用博客园牛人的解释:
node.js采用单线程异步非阻塞模式,也就是说每一个计算独占cpu,遇到I/O请求不阻塞后面的计算,当I/O完成后,以事件的方式通知,继续执行计算2。
但nodejs真的是单线程吗?其实只有js执行是单线程,I/O显然是其它线程。js执行线程是单线程,把需要做的I/O交给libuv,自己马上返回做别的事情,然后libuv在指定的时刻回调就行了。其实简化的流程就是酱紫的!细化一点,nodejs会先从js代码通过node-bindings调用到C/C++代码,然后通过C/C++代码封装一个叫 “请求对象” 的东西交给libuv,这个请求对象里面无非就是需要执行的功能+回调之类的东西,给libuv执行以及执行完实现回调。
你可以发现,其实nodejs在执行的时候,并不是一个挨着一个去执行,而是,以一个队列的形式,封装一个任务就回来继续回来往下走,而那个封装的任务是交给底层的线程池去完成的。这是最骚的。
其次,nodejs可以开启子进程,虽然也是多线程,但是它提供了一个主从关系。(你只需要理解nodejs也可以才用多线程模式运行就行了)所以,nodejs用来处理高并发是完全不在话下的。
好了,最后上几个例子大家体验一下吧。
同步进程:
var fs=require("fs");
function foo(){
function beginAnotherTask(){
var file=fs.createReadStream("./yishengyouni.mp3");
file.on("data",function(data){
console.log("读取到%d字节。",data.length)
});
process.nextTick(beginAnotherTask);
}
}
var file=fs.createReadStream("./yishengyouni.mp3");
file.on("data",function(data){
console.log("读取到%d字节。",data.length)
});
foo();
使用next.Tick方法可以讲一个任务,推迟到下一个异步或者同步方法的执行时。
多进程
使用child_process的spawn方法开启多个进程
并通过spawn中的stdio属性来关联两个进程(管道)
var cp=require("child_process");
var sp1=cp.spawn("node",["test1.js","one","tow","three","four"],{cwd:"./test"});
var sp2=cp.spawn("node",["test2.js"],{stdio:"pipe"});
sp1.stdout.on("data",function(data){
console.log("子进程标准输出:"+data);
sp2.stdin.write(data);
});
sp1.on("exit",function(code,signal){
console.log("子进程退出:"+code);
process.exit();
});
sp1.on("error",function(err){
console.log("子进程开启失败:"+err);
process.exit();
})
process.stdout.write("子进程当前工作目录为:"+process.cwd());
process.argv.forEach(function(val,index,array){
process.stdout.write("\r\n"+index+":"+val);
})
var fs=require("fs");
var out=fs.createWriteStream("./message.txt");
process.stdin.on("data",function(data){
out.write(data);
})
process.stdin.on("end",function(data){
process.exit();
})
注意:目录的结构
test2是在test文件夹外面的。