从前端到后端,龙哥讲nodejs基础(七)

服务器


当我们从一个前端或者后端走向全栈的时候,都会遇到一个问题:我人生中的第一台云主机(服务器)应该买什么配置的。

是不是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文件夹外面的。

你可能感兴趣的:(从前端到后端,龙哥讲nodejs基础(七))