简介:NodeJS声称其目标是“提供一种构建可伸缩网络程序的简单方法”,那么它的出现是为了解决什么问题,它的优缺点是什么,它适用于什么场景呢?
本文讨论了个人使用经验中的这些问题。
一. NodeJS的特点
让我们来看看NodeJS官网的介绍:
Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.
其特点为: 1. Javascript运行环境
2. 依赖于Chrome V8引擎代码解释
3. 事件驱动
4. 非阻塞I/O
5. 适用于实时数据交互应用
6. 单过程,单线程
二. NodeJS带来的解决系统瓶颈的方案
它的出现确实为我们解决现实中的系统瓶颈提供了新的思路和解决方案。让我们看看它能解决什么问题。
1. 并发连接
例如,想象一下我们在银行排队处理业务的场景。让我们看看以下两个模型。
(1)系统线程模型:
这个模型的问题很明显。服务端只有一个线程,只能处理一个并发请求(用户)到达。其余的必须先等待。这就是阻塞。您正在享受阻止以下请求的服务请求。
(2)多线程、线程池模型:
该模型比上一个模型有所进步。它调整了服务端线程的数量,以提高对并发请求的接收和响应。然而,当并发量较高时,请求仍需等待,这是一个更严重的问题。在代码层面,让我们来看看客户端请求和服务端通信的过程:
每次服务端与客户端建立连接,都应为连接分配一套支持资源,主要反映在系统内存资源中。以PHP为例,维护一个连接可能需要20M的内存。这就是为什么通常有大量的并发性,需要打开更多的服务器。
那么NodeJS是如何解决这个问题的呢?让我们来看看另一个模型,想象一下我们在快餐店点餐的场景。
(3)异步、事件驱动模型
我们还需要发起请求,等待服务器端响应;但与银行的例子不同,这次我们点餐后得到了一个号码。当我们得到这个号码时,我们经常在这个位置等待,我们以后的请求将继续处理。我们还得到了一个号码,然后等待,接待员可以一直处理。
当食物做号码时,我们会喊号码。我们得到了自己的食物,并进行了后续处理(进食)。在NodeJS中,这种喊号的动作被称为回调(Callback),可以在事件(做饭,I/O)处理完成后,继续执行以下逻辑(进食),反映了NodeJS的显著特点。异步机制和事件驱动的整个过程并没有阻止新用户的连接(订购),也不需要维护订购用户与厨师之间的连接。
基于这种机制,理论上有用户要求连接,NodeJS可以响应,所以NodeJS可以支持比Java更多的支持、虽然PHP程序的并发量更高,但维护事件队列也需要成本。因为NodeJS是单线程,事件队列越长,响应时间越长,并发量仍然无法提高。
总结NodeJS如何解决并发连接的问题:改变连接到服务器的方式,每个连接发射(emit)在NodeJS引擎过程中运行的事件(Event),将其放入事件队列中,而不是为每个连接生成一个新的OS线程(并为其分配一些配套内存)。
2. I/O阻塞
NodeJS解决的另一个问题是I/O阻塞。看看这样的业务场景:需要从多个数据源中提取数据,然后进行处理。
(1)串行获取数据是我们的一般解决方案,以PHP为例
如果需要1S才能获得profile和timeline操作,那么串行获取需要2S。
(2)NodeJS非阻塞I/O,发射/监控事件控制执行过程
当NodeJS遇到I/O事件时,它将创建一个线程来执行,然后主线程将继续执行。因此,如果将profile动作触发I/O事件,将立即执行timeline动作。如果每个动作都需要1S,则总时间为1S。I/O操作完成后,发射一个事件,profile和timeline,接收后,事件代理继续执行以下逻辑,这是NodeJS非阻塞I/O的特点。
总结一下:Java、PHP也有办法实现并行请求(子线程),但NodeJS通过回调函数(Callback)和异步机制会做得很自然。
三. 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
四. 适合NodeJS的场景
1. RESTful API
这是NodeJS最理想的应用场景,可以处理成千上万的连接,没有太多的逻辑,只需要请求API,组织数据返回。本质上,它只是从数据库中找到一些值,并形成一个响应。因为响应是少量的文本,入口请求也是少量的文本,所以流量不高,机器甚至可以处理最繁忙的公司的API需求。
2. UI层统一Web应用
目前MVC架构,从某种意义上说,Web开发有两个UI层,一个是我们最终在浏览器中看到的,另一个是在Server端生成和拼接页面。
不讨论这种架构是好是坏,但还有另一种实践,面向服务的架构,更好地依赖前后端的分离。如果所有的关键业务逻辑都被封装成REST调用,这意味着只需要考虑如何使用这些REST接口在上层构建特定的应用程序。那些后端程序员不担心具体数据是如何从一个页面传输到另一个页面的,他们不需要关心用户数据更新是通过Ajax异步获取还是刷新页面。
3. Ajax请求的大量应用程序
例如,个性化应用程序,每个用户看到不同的页面,缓存故障,需要在页面加载时启动Ajax请求,NodeJS可以响应大量的并发请求。 总之,NodeJS适用于高并发,I/O密集,少量业务逻辑场景。
五. 结尾
事实上,NodeJS几乎可以实现所有的应用,我们所考虑的是它是否适合它。