Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Node.js的核心概念——单线程,非阻塞,事件驱动 #2

Open
jawil opened this issue Apr 29, 2017 · 2 comments
Open

Node.js的核心概念——单线程,非阻塞,事件驱动 #2

jawil opened this issue Apr 29, 2017 · 2 comments

Comments

@jawil
Copy link
Owner

jawil commented Apr 29, 2017

一些文章

1、这就是所谓的Node.js------单线程,非阻塞,事件驱动
2、Node.js 探秘:初识单线程的 Node.js
3、进程和线程
4、同步、异步、阻塞及非阻塞
5、进程 线程 协程 管程 纤程 概念对比理解
6、I/O模型 阻塞 非阻塞 同步 异步概念对比区分

一些概念

1、什么是Node.js?

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.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

我们可以注意到:Node.js是一个平台,它构建在Chrome的JavaScript引擎(也就是大名鼎鼎的V8引擎)之上,它能快速构建有拓展性的网络应用程序。这里官方用的“网络应用程序”,整个描述没有提到“web","sever"等等概念。

举个栗子,类比来说,概念上,Node.js相当于.net,JVM或者PythonVM。它是一个运行平台,只不过它运行的是JavaScript语言而已。类似的,.net一般运行C#,VB等编译的IL,JVM一般运行Java编译成的字节码,而PythonVM解释运行Python代码,RubyVM解释运行Ruby代码。严格来说Node.js更像PythonVM,RubyVM,因为他们都是解释运行的动态语言。

回到刚才的话中:,有几个是重点:

  1. node.js是一个构建在Chrome JavaScript运行环境的平台,这是很重要的一点,node.js并不是一门语言,而是一个平台。
  2. node.js致力于构建速度快、稳定的网络程序更简单。
  3. node.js具有事件驱动和非阻塞I/O的特色,使之轻量级并且高效率。
  4. node.js非常适合在分布式设备运行数据密集型实时应用程序。

如果你能掌握住这几点,我觉得算是掌握了Node.js的真谛了。

Node.js核心主要是两部分组成的:

  1. V8引擎,它负责把JavaScript代码解释成本地的二进制代码运行。
  2. libuv,类似Windows上的窗口消息机制,它主要负责订阅和处理系统的各种内核消息。而且它也实现了消息循环(是不是很耳熟?没错,这个几乎就和Windows的窗口消息循环是一个概念。)它的前身是linux上的libev,专门封装linux上的内核消息机制。后来Node.js重写了它,并在Windows上使用iocp技术重新实现了一遍。所以Node.js现在能跨平台运行在Windows上了。

可以说,Node.js其实就是libuv的一个应用而已。简单的说Node.js只是把JavaScript解释成C++的回调,并挂在libuv消息循环上,等待处理。这样就实现了非阻塞的异步处理机制。

那么为什么是JavaScript而不是其他语言呢?很简单,因为JavaScript的闭包。这非常适合做回调函数。因为我们一般都希望当回调发生时,闭包能记住它原来所在的执行上下文。这就是闭包最好的应用场景。

2、Why Node.js?

Node.js出现确实能为我们解决现实当中系统瓶颈提供了新的思路和方案,下面我们看看它能解决什么问题?

并发连接

举个栗子,想象一个场景,我们在银行排队办理业务,我们看看下面两个模型。

(1)系统线程模型:
这种模型的问题显而易见,服务端只有一个线程,并发请求(用户)到达只能处理一个,其余的要先等待,这就是阻塞,正在享受的请求阻塞后面的请求了。

(2) 多线程、线程池模型:
这个模型已经比上一个有所进步,它调节服务端的数量来提高对并发请求的接收和响应,但并发量高的时候,请求仍然需要等待,他有个更严重的问题。到代码层面上来讲,我们看看客户端请求与服务通讯的过程:服务端与客户端每建立一个连接,都要为这个连接分配一套配套的资源,主要体现为系统内存资源,以PHP为例,维护一个连接可能需要20M的内存。这就是为什么一般并发量一大,就需要多开服务器。那么Node.js是怎么解决这个问题呢?我们来看看另外一个模型,想象一下我们在快餐店吃饭的场景。

(3)异步、事件驱动模型
我们同样是发起请求,等待服务器端响应;但是与银行例子不同的是,这次我们点完餐后拿到了一个号码,拿到号码,我们往往会在位置上等待,而在我们后面的请求会继续得到处理,同样是拿了一个号码然后到一旁等待,接待员能一直进行处理。等待饭菜做好了,会喊号码,我们拿到了自己的饭菜,会进行后续的处理(吃饭)。这个喊号码的动作在Node.js中叫做回调(CallBack),能在事件(烧菜,I/O)处理完成后继续执行后面的逻辑(吃饭),这体现了Node.js显著特点,异步机制、事件驱动整个过程没有阻塞新用户的连接(点餐),也不需要维护已经点餐的用户与厨师的连接。基于这样的机制,理论上陆续有用户请求连接,Node.js都能够进行响应,因此Node.js能支持比Java,PHP程序更高的的并发量虽然维护事件队列也需要成本,再由于Node.js是单线程,事件队列越长,得到响应的时间就越长,并发量上去还是会力不从心。

总结一下Node.js是怎么解决这个问题的:

更改到连接服务器的方式,每个连接发射(emit)一个在Node.js引擎进程中运行的事件(Event),放进事件队列当中,而不是为每个连接生成一个新的OS线程(并未其分配一些配套内存)。

I/O阻塞

Node.js解决的另外一个问题是I/O阻塞,看看这样的业务场景:需要从多个数据源拉取数据,然后进行处理。

①串行获取数据,这是我们一般的解决方案,一PHP为例的I/O阻塞-PHP为例,假如获取profile和timeline操作各需要1s,那么串行获取就需要2s。
②Node.js非阻塞I/O事件会创建一个线程去执行,然后主线程会继续往下执行,因此,拿profile的动作触发一个I/O事件,马上就会去执行拿timeline的动作,两个动作并行执行,加入各需要1s,那么总的时间就是1s。它们的I/O操作执行完成后,会发射一个事件,profile和timeline,事件代理接收后继续往下执行后面的逻辑,这就是Node.js非阻塞I/O的特点。

总结一下:Java、PHP也有办法实行并行请求(子线程),但Node.js通过回调函数(CallBack)和异步机制会做的很自然。

3、Node.js优缺点

优点:
  1. 高并发(最重要的优点)
  2. 适合I/O密集型应用(静态资源服务器、blog、聊天室)
缺点:
  1. 不适合CPU密集型应用(模板渲染、压缩/解压缩、加/解密);
    CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循坏),将会导致CPU时间片不能释放,使得后续I/O无法发起;解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用发起;
  2. 只支持单核CPU,不能充分利用CPU
  3. 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃,其中2和3的原因:单进程,单线程解决方案:
    i. Ngnix反向代理,负载均衡,开多个进程,绑定多个端口;
    ii. 开多个进程监听同一个端口,使用cluster模块;
  4. 开源组件质量参差不齐,更新快,向下不兼容。
  5. Debug不方便,错误没有stack trace。
@jawil jawil changed the title Node.js的核心概念------单线程,非阻塞,事件驱动 Node.js的核心概念——单线程,非阻塞,事件驱动 Apr 29, 2017
@yilikun
Copy link

yilikun commented Jun 28, 2018

那么为什么是JavaScript而不是其他语言呢?很简单,因为JavaScript的闭包。
你好,这句话我有些疑问,其他语言也有闭包啊,比如python,为什么只是因为js的闭包呢?希望得到解答~

@unadlib
Copy link

unadlib commented Jul 20, 2018

这句话不够准确,应该是因为 node.js中的libuv实现了 基于事件驱动的 事件循环,因此因为单线程,而有非阻塞的feature,事件轮询 中 每个回调函数 正是因为js的闭包才完整包含的函数执行上下文。而不是因为js语言 feature。因果关系不够准确而已。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants