关于nodejs单线程

本文详细探讨了Node.js的单线程模型如何处理高并发,以及在处理I/O密集型和CPU密集型任务时的策略。Node.js通过事件驱动和线程池实现异步I/O,特别适合RESTful API和实时应用。同时,文章指出Node.js在CPU密集型任务中的不足,提出了解决方案,包括编写C/C++扩展和利用子进程。此外,还详细解释了read、select、poll、epoll等异步I/O模型,并讨论了非I/O异步API如setTimeout、setImmediate和process.nextTick。最后,文章比较了不同服务器模型,强调了事件驱动在高性能服务器中的重要性,并指出Node.js的跨平台兼容性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Node是使用C++语言写的一款JavaScrip解析器。

高并发

一般来说,高并发的解决方案就是多线程模型,服务器为每隔客户端请求分配一个线程,使用同步I/o,比如Apache就是这种策略,由于I/O一般都是耗时操作,因为这种策略很难实现高性能,但非常简单,可以实现复杂的交互逻辑。

而事实上,大多数网站的服务器都不会做太多计算,它们只是接收请求,交给其他服务(比如从数据库读取数据),然后等着结果返回再发给客户端。因此Node.js针对这一事实采用了单线程模型来处理,它不会为每个接入请求分配一个线程,而是用一个主线程处理所有的请求,然后对I/O操作进行了异步处理,避开了创建、销毁线程以及在线程间切换所需的开销和复杂性。

Node.js使用场景

RESTful API:这是适合Node的理想情况,因为您可以构建它来处理数万条连接。它仍然不需要大量逻辑;它本质上只是从某个数据库中查找一些值并将它们组成一个响应。由于响应是少量文本,入站请求也是少量的文档,因此流量不高,一台机器甚至也可以处理最繁忙的公司的API需求。

实时程序:比如聊天服务,聊天应用程序是最能体现Node.js优点的例子:轻量级、高流量并且能良好的应对跨平台设备上运行密集型数据(虽然计算能力低)。同时,聊天也是一个非常值得学习的用例,因为它很简单,并且涵盖了目前位置一个典型的Node.js会用到的大部分解决方案。

Node.js的软肋

至此,我们对Node.js应该有了一个简单而又清晰的认识,但Node.js并不是说明都能做。

上面提到,如果是I/O任务,Nodejs就把任务交给线程池来异步处理,高效简单,因此Node.js适合处理I/O密集型任务,当碰到CPU密集型的任务时,就是只用CPU计算的操作,比如要对数据加解密(node.bcrypt.js),数据压缩和解压(node-tar),这时Node.js就会亲自处理,一个一个的计算,前面的任务没有执行完,后面的任务只能干等着,如下图所示:

在事件队列中,如果前面的CPU计算任务没有完成,那么后面的任务就会被阻塞,出现响应缓慢的情况,如果操作系统本身就是单核,那也就算了,但现在大部分服务器都是多CPU或多核的,而Node.js只有一个EventLoop,也只占用一个CPU内核,当Node.js被CPU密集型任务占用,导致其他任务被阻塞时,却还有CPU/内核处于空闲状态,造成资源浪费,因此Node.js并不适合CPU密集型任务。

Node.js哪些操作会交给线程池?

网络调用、文件系统任务、DNS 查找等异步完成的事情实际上并不由主线程处理。

CPU密集型场景的解决方案

关于CPU密集型应用,Node的一部I/O已经解决了在单线程上CPU与I/O之间阻塞无法重叠利用的问题,I/O阻塞造成的性能浪费远比CPU的影响小。对于长时间运行的计算,如果它的耗时超过了普通阻塞I/O的耗时,那么应用场景就需要重新评估,因为这类计算比I/O阻塞还影响效率,甚至说就是一个纯计算的场景,根本没有I/O。此类应用场景或许应当采用多线程的方式进行计算。Node虽然没有提供多线程用于计算支持,但是还是有以下两个方式来充分利用CPU。

  • 通过编写C/C++扩展的方式更高效地利用CPU,将一些V8不能做到性能极致的地方通过C/C++来实现。由上面的测试结果可用看到,通过C/C++扩展的方式实现的计算,速度比JAVA快。
  • 如果单线程的Node不能满足需求,甚至用来C/C++扩展后还觉得不够,那么通过子进程的方式,将一部分Node进程当作常驻服务进程用于计算,然后利用进程间的消息来传递结果,将计算与I/O分离,这样还能充分利用多核CPU。

异步I/O和事件驱动

异步I/O,事件驱动和单线程,它们是Node的基调。

Node.js实现异步的核心是事件驱动。也就是说,它把每一个任务都当成事件来处理,然后通过Event Loop模拟了异步的效果。

总结以上过程我们发现,Node.js的主线程就是一个单线程,它接收请求后并没有直接做处理,而是放到了事件队列中,然后去接收其他请求了,空闲时再通过Event Loop来处理这些事件,从而实现了异步的效果。当然对于IO类任务还要依赖于系统层面的线程池来处理。因为,我们可以简单理解为:Node.js本身是一个多线程平台,而它对JS层面的任务处理是单线程

非阻塞I/O返回后,CPU得时间片可用用来处理其他事务,此时得性能提升是明显得。但非阻塞I/O也存在一些问题。由于完整得I/O并没有完成,立即返回得并不是业务层期望得数据,而仅仅是当前调用得状态。为了获取完整得数据,应用程序需要重复调用I/O操作来确认是否完成。这种重复调用判断操作是否完成的技术叫做轮询。

任何技术都并非完美的,阻塞I/O造成CPU等待浪费,非阻塞带来的麻烦却是需要轮询去确认是否完全完成数据获取,它会让CPU处理状态判断,是对CPU资源的浪费,我们且看轮询技术是如何演进的,以减少I/O状态判断的CPU损耗。

read

最原始,性能最低的一种。通过重复调用来检查I/O的状态。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李小白20200202

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值