nodejs第一天

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

  我们来到Node的官方网站的时候就能看到这么一句话,这句话体现了node的几大特点,我们来一一细看。

一、 Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

  我们学习一个东西,首先我们要知道这东西是用来干什么的。以前我听说node的时候以为nodejs是一个框架,当了解到node之后就认为这只是在服务端的javascript,但是事实的真相是这样的么?我们来看一看上面这段话。Node.js是一个构建在Chrome的V8引擎上的javascript的运行时,可能你不太能理解运行时是什么,其实在我的理解中就和Java的JRE(Java Runtime Environment)是差不多的,他提供了API环境、运行环境,让我们的js在服务端可以跑起来,同时提供了可以对文件系统、网络等等进行操作的api,这是让前端工程师无比兴奋的突破。

二、 Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

  接下来的这句话体现了Node的几大特性:

  • 事件驱动
  • 非阻塞I/O模型
  • 单线程(里面没提,但是我觉得这也是一大特性)

  再说上面的几个特性之前,我们先来看一看,在我学习的途中经常会看到有人说Node在处理高并发、I/O密集型服务是具有优势的,那么我的疑问是I/O密集型是啥,搜索资料后发现,跟I/O密集相对应的是CPU密集,我们可以看一看这两者之间的区别。

  • I/O密集:如果一个程序大部分消耗的时间都是用来对文件的I/O操作,数据库的操作,或者是网络操作,存取这一类的操作,那么我们就可以说这是I/O密集型的服务。
  • CPU密集: 如果一个程序的时间都消耗在计算,解压,压缩等等需要CPU来进行操作的话,那么我们就可以说这是一个CPU密集型的服务。

  好了我们看完这两者之间的区别,我们也就能了解,为啥Node适合web开发,你想一想我们在进行web开发的时候,我们进行的都是些什么操作,静态资源读取、网络操作、数据库的增删改查,我们可以发现web场景中I/O操作是最耗时的操作。现在CPU的提速我们执行指令的速度是很快的,但是我们进行I/O操作还是非常耗时的。我们可以看到web是一个很典型的I/O密集型服务,那么我们为什么说Node就非常适合web场景呢?我们等会儿再谈。

  下面我们来看一看高并发的情况,在《深入浅出NodeJS》这本书中我们能看到朴灵老师对服务模型的见解,我觉得很能说明问题,我们一起来看一看:

  1. 石器时代:同步
      在很久很久以前,我们服务器是同步的,一次只能服务一个请求,就比如说我们一个饭店,请了一个厨子一个服务员,我们现在只有一个厨子,所以我们现在只能接收一个客人的点餐,等上个客人的做好了我们才能继续进行下面一位客人的点餐,我们都知道这样的效率肯定是不行的,所以又有了下面的演进。
  2. 青铜时代:复制进程
      我们可以通过进程的复制,一个进程服务一个请求,这样我们就能够服务多个请求了,但是梦想是很美好的,理想很丰满。继续用上面的例子,我们饭店大起来了,一个厨子这样已经忙不过来了,这时候,我们能想到的是什么,再多请几个厨子,这样也能够解决多个客人的问题,但是我们想了一下,请这么多厨子,工资谁发啊,太贵了。这也是复制进程的一个缺点,进程的复制是非常昂贵的,我们要复制进程内的状态,而且进程内还会有很多相同的状态会造成浪费,那么又该怎么办呢?
  3. 白银时代:多线程
      我们从上面的角度上来想,我们速度慢的操作也就是读取的操作,不需要很厉害的厨子,小厨子就行了,于是乎我们就炒掉了大厨,用大厨的钱请了几个小厨子,这样我们的小厨子也能够进行炒菜,所以我们接到点餐之后,就给这几个小厨子做。但是呢我们一个服务员对应的厨子是有限的,我们可以想象,点菜这个操作是非常快的,可能服务员点完菜之后就开始玩了,等厨子做好了才会端上来,这就造成了CPU的浪费,同时呢,对于多线程来说,占用内存空间,线程的上下文切换等等问题也是无法做到很好的满足需求。
  4. 黄金时代:事件驱动
      为了解决高并发问题,基于事件驱动的服务模型就出现了,就像Node和Nginx都是基于事件驱动的,采用单线程就避免了不必要的内存开销和上下文切换。看上面的例子,其实我们雇一个服务员,排队叫号,然后就接待下一个客人,不用等这个客人的菜好了才服务下一个,等厨子做好了菜就直接给服务员说菜做好了,然后叫号,客人来取就OK了。

nodejs第一天_第1张图片

单线程

  借用一张老师的图,其实这张图其实我们能看得很清楚,我们JS是单线程的,node中也是这样的,客户端的N个请求,我们接收到之后,是直接将耗时得操作放到了后面的线程池里去操作,使用单线程可以避免内存的消耗,以及上下文的切换。同时,我们利用事件驱动,可以在后面的线程I/O操作完成后以事件的方式通知主线程,然后返回结果。
  其实说到这里,有些人就和我一样困惑了,不是说nodejs是单线程么,怎么又冒出了多线程。其实我们想一想也能够明白,如果nodejs只是靠单线程工作,那么他怎么完成高并发的web服务呢?我们可以这么说,nodejs的单线程是针对主进程的,而异步操作、I/O操作等等都是靠操作系统底层的多线程调度了,nodejs的单线程只是负责调度,就像饭店的服务员一样,只负责接单,做菜的都是后台的厨师。还有就是nodejs是单线程,那么现在的多核CPU该怎么去使用呢?其实不用我们担心,node是考虑了这样的情况的,nodejs是有专门的模块来进行操作的。

事件驱动

  那么事件驱动又是个什么东西呢?其实,我们在做前端开发的时候是经常和事件打交道的,比如ajax,我们发起一个请求,然后监听事件,成功之后就执行回调。其实这也是node的一大特点,就像node在接收请求一样的,node接收到请求之后,将对应的I/O操作交给操作系统,然后监听事件,当I/O操作完成之后,触发事件执行回调拿到数据。

非阻塞I/O

  阻塞I/O就是当用户发一个读取文件的操作的时候,进程就会被阻塞,直到要读取的数据全部准备好返回给用户。那非阻塞I/O呢,就是用户发起一个读取文件操作的时,函数立即返回,不作任何等待,进程继续执行,当读取操作完成之后,主进程再去拿数据。那程序如何知道要读取的数据已经准备好了呢?最简单的方法就是轮询,这个在朴灵老师的书里面也提到了几种方式。


  今天就简单的学习了一下node,如果有什么地方不对的请多指教,也是才学习nodejs。

你可能感兴趣的:(nodejs第一天)