node和js有什么关系
由于JavaScript是单线程运行的原因,程序难免会发生请求阻塞的情况,试想如果请求一直没有得到回复,那就会一直处于卡死状态。这是很不好的体验,所以人们就想出了一种异步的方案,等待请求资源返回后执行回调函数。
但是Node的功能远远不止这样,Node打破了JavaScript代码只能在浏览器运行的局面,使得服务器也能运行JavaScript代码。进一步说明了JavaScript的功能不会局限于脚本,而是一门真正的后端语言。
node.js
node并不是一门具体的编程语言,而是一种环境,一个可以在服务端、跨平台的JavaScript环境;
node和浏览器一样,都基于V8引擎开发,V8引擎是为了解析js代码产生的,在node环境中同样可以解释js代码;
简单来说,node实现了用JavaScript开发后端程序,去除了JavaScript只能在浏览器端运行的限制,几乎可以实现其他后端语言实现的所有功能;
node的优势
1、Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发
Node打破了过去JavaScript只能在浏览器中运行的局面。前后端编程环境统一,可以大大降低开发成本。
2、NodeJs超强的高并发能力
NodeJs的首要目标是提供一种简单的、用于创建高性能服务器及可在该服务器中运行的各种应用程序的开发工具。
首先让我们来看一下现在的服务器端语言中存在着什么问题。在Java、PHP或者.NET等服务器语言中,会为每一个客户端连接创建一个新的线程。而每个线程需要耗费大约2MB内存。也就是说,理论上,一个8GB内存的服务器可以同时连接的最大用户数为4000个左右。要让web应用程序支持更多的用户,就需要增加服务器的数量,而web应用程序的硬件成本当然就上升了。
NodeJs不为每个客户连接创建一个新的线程,而仅仅使用一个线程。当有用户连接了,就触发一个内部事件,通过非阻塞I/O、事件驱动机制,让Node.js程序宏观上也是并行的。使用Node.js,一个8GB内存的服务器,可以同时处理超过4万用户的连接。
3、实现高性能服务器
严格地说,Node.js是一个用于开发各种web服务器的开发工具。在Node.js服务器中,运行的是高性能V8 JavaScript脚本语言,该语言是一种可以运行在服务器端的脚本语言。
那么,什么是V8 JavaScript脚本语言呢?该语言是一种被V8 JavaScript引擎所解析并执行的脚本语言。V8 JavaScript引擎是由Google公司使用C++语言开发的一种高性能JavaScript引擎,该引擎并不局限于在浏览器中运行。Node.js将其转用在了服务器中,并且为其提供了许多附加的具有各种不同用途的API。例如,在一个服务器中,经常需要处理各种二进制数据。在JavaScript脚本语言中,只具有非常有限的对二进制数据的处理能力,而Node.js所提供的Buffer类则提供了丰富的对二进制数据的处理能力。
另外,在V8 JavaScript引擎内部使用一种全新的编译技术。这意味着开发者编写的高端的 JavaScript 脚本代码与开发者编写的低端的C语言具有非常相近的执行效率,这也是Node.js服务器可以提供的一个重要特性。
4、开发周期短、开发成本低、学习成本低
Node.js自身特性,是使用最小的硬件成本,追求更高的并发,更高的处理性能。
node.js特点
根据官网的解释,大概有以下几点:
- 它是一个Javascript运行环境
- 依赖于Chrome V8引擎进行代码解释
- 异步事件驱动
- 非阻塞I/O
- 轻量、可伸缩,适于实时数据交互应用
- 单进程,单线程(这里指主线程)
- 性能出众
1.V8引擎
node环境中对Google V8引擎进行了进一步的封装,V8引擎可以使js运行速度极快,性能特别好;
node不仅仅是对V8引擎进行了封装,也对js不能照顾到的场景进行了相应的优化:
例如,在服务器环境中,处理二进制数据通常是必不可少的,但Javascript对此支持不足,因此,V8.Node增加了Buffer类,方便并且高效地 处理二进制数据。因此,Node不仅仅简单的使用了V8,还对其进行了优化,使其在各环境下更加给力。
2.事件驱动
事件驱动,是指在持续事务管理过程中,进行决策的一种策略,即跟随当前时间点上出现的事件,调动可用资源,执行相关任务,使不断出现的问题得以解决,防止事务堆积。
Nodejs设计思想中以事件驱动为核心,事件驱动在于异步回调,他提供的大多数api都是基于事件的、异步的风格。而事件驱动的优势在于充分利用系统资源,执行代码无须阻塞等待某种操作完成,有限的资源用于其他任务。事件驱动机制是通过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文的切换。
关于事件驱动中的"单线程高效率维护事件循环队列的概念" ,可以去看一下我的另一篇文章:node事件循环,相信可以有一点启发;
3.异步/非阻塞I/O
Nodejs提供的很多模块中都是异步执行的。比如,文件操作的函数。
当我们尝试去访问数据库的时候就会向服务器发送请求,时间长短取决于网络和数据库的性能如何。假如我们没有异步的机制,那我们就必须等待结果返回,什么也做不了。这就是单线程的缺点,会阻塞后续代码的执行,降低了程序的效率。
现在异步的机制是当我们执行请求的时候,立即执行后面的代码而不需要等待,请求结果放在回调函数当中,提高了效率。当I/O操作执行完毕之后会以事件的形式通知执行I/O操作的线程,“你要的结果处理好了,快来拿吧!”。
由于会有很多这样的异步请求,那就必须要有先后的顺序,这就是Node中的事件循环机制,JavaScript的不同。事件循环机制依次检查是否还有没有处理的回调函数,依次执行。
非阻塞 I/O,也叫异步 I/O,显然对应的就是阻塞式 I/O。 传统的服务器语言大多是多线程、阻塞式 I/O。这也是 Node 与众不同的地方,对于传统的服务器语言,在与用户建立连接时,每一个连接都是一个线程。 当有十万个用户连接时,服务器上就会有十万个线程。而阻塞式 I/O 是指,当一个线程在执行 I/O 操作时,这个线程会阻塞,等待 I/O 操作完成后继续执行。
举个例子可以更好理解,比如我们到一个餐馆吃饭,这个餐馆比较高级,服务员是一对一服务(每个用户都是一个线程),从我们坐下开始,服务员就把菜单给你,然后在旁边等你点菜(等待 I/O 操作),当你看完菜单,把要点的菜告诉服务员( I/O 操作结束后线程继续执行)。在你看菜单的过程中,服务员其实是被闲置的,如果你一直看,他就会一直等,直到你点完( I/O 操作结束)。这就是阻塞式 I/O。 阻塞式 I/O 必须要多线程,就这个例子来看,如果只有一个服务员(单线程),那当店里同时有十个顾客时,他需要一个一个等待顾客看菜单然后点菜,那后面的顾客要等待的时间就会很长,显然这个餐馆的服务就会很差(服务器性能差)。
所以传统的服务器都是多线程、阻塞式 I/O,也就是相当于有多个服务员(线程),每个进来的顾客都分配到一个服务员(线程),然后你看菜单时在旁边等候(阻塞式 I/O )。很明显这样的服务是让顾客最舒服的,但是对于餐馆老板来说很难受,因为要雇佣大量的服务员。因此传统的方式成本是比较大的。要有性能足够好的服务器才能支撑大量的线程。但他的优点也很明显,比如某一桌客人与服务员发生争吵(线程崩了!),不会影响到其他顾客,因为每一桌都有专门的服务员负责,因此只会对当前用户(线程)产生影响。
上面的例子应该可以很好地理解多线程、阻塞式 I/O 。
而 node 的特性是单线程、非阻塞式 I/O 。node 最大的优势就是性能强,同样的服务器性能使用 node 可以比传统的服务器语言多容纳一百倍的用户(对于不同的任务有不同的差别, I/O 操作越多,node优势越明显(I/O密集型),如果都是 CPU 计算任务,那他俩几乎没有区别(上面的例子中,忽略顾客的看菜单时间)。
还是用上面的例子再比喻一下单线程、非阻塞式 I/O 。这应该是个规模比较小的餐馆,或者说老板比较穷,雇不起大量的服务员,因此只能雇佣一个服务员。当有顾客来时,服务员把菜单送过去,顾客开始看菜单( I/O 操作),这个时候,服务员是被释放了的,他不用等待顾客看菜单,服务员说:“您先看着菜单,点好了叫我”(回调函数)。这个时候这个服务员就可以抽身去服务其他的顾客。用这种模式的话,一个服务员就可以服务多位顾客,而且不需要等待 I/O ,只需要随时监听就行了,顾客点完后会主动叫服务员(执行回调函数)。
单线程、非阻塞式 I/O 的优势就是性能强,一个人服务员就可以解决大量顾客。但是他的缺点也很明显,比如有一桌顾客和服务员又吵架了(线程崩了!),那这些顾客就都完了,因为所有人都在等这一个服务员。也就是说,如果线程崩掉了,那与这个服务器连接的所有用户都会崩溃。
4.单线程
Nodejs跟Nginx一样都是单线程为基础的,这里的单线程指主线程为单线程,所有的阻塞的全部放入一个线程池中,然后主线程通过队列的方式跟线程池来协作。
nodejs所谓的单线程,只是主线程是单线程,所有的网络请求或者异步任务都交给了内部的线程池去实现,本身只负责不断的往返调度,由事件循环不断驱动事件执行。
5.性能出众
底层选择用c++和v8来实现的,这意味着面对大规模的http请求,nodejs是凭借事件驱动来完成的,性能部分是不用担心的,并且很出色
nodejs优缺点
优点:
1. 高并发(最重要的优点)
2. 适合I/O密集型应用
缺点:
1. 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
2. 只支持单核CPU,不能充分利用CPU
3. 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
原因:单进程,单线程
解决方案:(1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
(2)开多个进程监听同一个端口,使用cluster模块;
4. 开源组件库质量参差不齐,更新快,向下不兼容
5. Debug不方便,错误没有stack trace
CPU密集型/I/O密集型
上面说到了node.js适合I/O密集型的场景,那么下面介绍以下这两种模式都是什么
CPU密集型 vs IO密集型
我们可以把任务分为计算密集型和IO密集型。
计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。
计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。
第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。
总之,计算密集型程序适合C语言多线程,I/O密集型适合脚本语言开发的多线程。
结尾
其实NodeJS能实现几乎一切的应用,我们考虑的点只是适不适合用它来做。