如何更好的利用Node.js的性能极限

通过使用非阻塞、事件驱动的I/O操作,Node.js为构建和运行大规模网络应用及服务提供了很好的平台,也受到了广泛的欢迎。其主要特性表现为能够处理庞大的并且高吞吐量的并发连接,从而构建高性能、高扩展性的互联网应用。然而,Node.js单线程的的工作方式及有限的可管理内存使得其计算性能十分有限,限制了某些场景中的应用。近日,Jut开发团队的工程师Dave Galbraith分享了他们所遇到的Node.js的限制以及超越这些限制的方法。接下来,本文就详细分析其所遇到的问题及解决思路。

首先,Jut团队所研发的产品称为操作数据中心(operations data hub)。该产品是一个专门为研发团队所设计的流分析平台,主要用于收集日志以及事件等操作数据,然后根据整体做分析和关联。其核心功能就是要能够同时处理实时数据、历史数据、结构和非结构数据。具体产品架构如下图所示。

从上图可以看出,该产品的核心就是数据引擎,包括底层大数据后端和JPC(Juttle Processing Core)两部分。其中,整体系统需要依赖ElasticSearchCassandra等这些大数据后端分系统,进行历史数据的处理和存储以及一般数据的复原和管理;JPC采用了Node.js,完成同等对待历史数据和实时数据、利用日志数据/度量数据/事件数据提出问题以及发送实时数据到浏览器来利用d3进行可视化等。而且,JPC负责运行Juttle程序。当用户点击Juttle程序时,浏览器把程序发送到JPC,将其转换为JavaScript进行执行。Galbraith提出,Jut团队选择JPC中使用Node.js的原因包括采用JavaScript等高级编程语言可以快速完成建模和迭代过程;鉴于程序前端采用JavaScript实现,后端同样采用JavaScript可以方便前后端配合和沟通;Node.js拥有强大的开源社区,使得开发团队可以有效利用社区的力量等三个方面。JPC就利用了社区中103个NPM包,同时也共享了自己开发的7个包。

尽管Node.js拥有着非常好的特性,JPC的开发团队还是遇到了一些Node.js不能直接解决的问题:

  1. Node.js的应用程序都是单线程的。这就意味着即使计算机是多核或多处理器的,node.js的应用程序也只能利用其中一个,大大限制了系统性能。
  2. 随着堆栈变大,Node.js的垃圾收集器变得非常低效。随着堆栈使用空间超过1GB,垃圾收集的过程开始变得非常慢,会严重影响程序的性能。
  3. 因为以上的问题,Node.js限制了堆栈所能使用的空间为1.5GB。一旦超过该范围,系统就会出错。 
    为了保证Jut系统的高效性,Jut团队想出了一些解决方案。

首先,针对Node.js单线程引起的性能低下问题,Jut团队采用了尽量避免利用Node.js进行计算的方式。JPC会把Juttle流图切割为一些子图,然后在Jut平台的更深层再进行高效执行。以ElasticSearch为例,在未优化之前,数据请求的流程为:ElasticSearch把相关数据从磁盘中取出->编码为JSON->通过HTTP协议发送给JPC->JPC解码JSON文件,执行预想的计算。然而,ElasticSearch拥有一种聚合(Aggregation)功能,能够跨数据集执行计算。这样,一次大的请求就可以优化为一个ElasticSearch聚合,避免了中间多次JSON转换以及Node.js针对大规模数据进行计算的过程。而且,ElasticSearch和Cassandra都是采用Java编写,可以有效利用多核或多处理器资源,实现高效率并行计算。总之,通过尽量避免在Node.js中进行计算的方式,Jut团队有效提高了系统的性能。

其次,关于堆栈空间问题。每当用户让Node.js服务器向其他服务器发送请求时,用户都会提供一些相应的函数,来对未来返回的数据进行处理。Node.js就会把这些函数放到event loop中,等待数据返回,然后调用相应的函数进行处理。这种类似中断的处理方式,可以大大提高单线程Node.js的效率。然而,一旦event loop中其中一个函数计算的时间过长,系统就会出现问题。以用户向Node.js发送从其他服务器中请求若干行的数据,然后对这些数据进行数学计算为例。如果请求的数据超过了1.5GB堆栈大小的限制,计算过程就会占用Node.js很长一段时间,甚至无法完成。由于Node.js为单线程,在这段时间内,新的请求或者新返回的数据只能放置在event loop的待办列表中。这样,Node.js服务器的反应时间将会大大增加,影响其他请求的正常处理。

为了解决该问题,Jut在任何可能的地方实现了分页(paging)。这就意味着,系统将不会一次读取大量数据,而是将其划分为若干小的请求。在这些请求中间,系统还可以处理器新的请求。当然,多次请求都需要一定的通信代价的。经过Jut团队的摸索,20000个点是比较合适的规模——系统仍然能够在若干毫秒中执行完毕,而且一般的请求也不需要进行大量分割。

针对这些问题,Galbraith分享了一个具体的使用案例。作为Jut的忠实客户,NPM一直伴随着Jut从alpha版本一直走到了现在的beta版本。NPM一个具体的任务就是找到所有包中过去两周下载量最大的前十名,然后在网站中以表格形式的显示。Juttle程序可以利用非常简单的代码完成该任务:

read -last :2 weeks: | reduce count() by package | sort count -desc | head 10 | @table

但是,Jut第一次跑该程序的时候就遇到了问题。经过调试发现,问题的原因在于JPC优化了read和reduce操作,将其合并为一个ElasticSearch聚合操作。由于聚合操作本身并不支持分页,而NPM的包数要超过数百万个,ElasticSearch就返回了一个超过百万个数组的巨大响应结果,总大小在几百MB。收到该响应后,JPC就试图一次处理完毕,导致内存空间使用超过了1.5GB的限制。垃圾收集器开始不断尝试回收空间。结果,处理时间超过了JPC内置的监控服务认为出现异常的阈值——60s。监控服务直接重启JPC,导致了NPM的任务一直无法完成。

为了解决该问题,Jut团队采用了模仿ElasticSearch针对聚合进行分页的方法。针对返回的包含大量信息的结果,JPC将其切分为可以方便处理的小块,一个个处理。在一些公开库的帮助下,修改后的JavaScript代码如下:

var points = perform_elasticsearch_aggregtion();`
Promise.each(_.range(points.length / 20000), function processChunk(n) {
        return Promise.try(function() {
        process(points.splice(0, 20000));
        }).delay(1);
    });

其中Promise.each(param1,param2)负责针对第一个参数param1中的每一个元素调用第二个参数中的函数param2_.range(num)函数接收一个数字num,返回该数字大小的数组。以包含100万个点为例,上述程序需要调用processChunk()函数50(points.length/20000=1000000/20000=50)次。每次调用负责把20000个点拉出数组,然后调用process()函数进行处理。一旦处理完毕,垃圾收集器就可以对这20000个点占用的空间进行回收。Promise.try()以一个函数作为参数,返回能够控制其参数中函数执行的对象。该对象的.delay(1)方法表示在多次调用中间允许处理器1ms的暂停去处理其他请求。经过这样的修改,程序只花费了大概20s的时间就完成了之前NPM的任务。而且,在此期间,服务器还对其他请求进行了响应。

全能程序员交流QQ群290551701,聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!

时间: 2024-08-23 09:24:14

如何更好的利用Node.js的性能极限的相关文章

利用Node.js对某智能家居服务器重构

原文摘自我的前端博客,欢迎大家来访问 http://www.hacke2.cn 之前负责过一个智能家居项目的开发,外包重庆一家公司的,我们主要开发服务器监控和集群版管理. 移动端和机顶盒的远程通信是用中间服务器完成交互,服务器使用MINA NIO框架,非阻塞式的,可以看看以前博客了解下某智能家居项目框架学习总结,或者其他资料JAVA NIO原理,基于MINA框架快速开发网络应用程序. 在移动端或者机顶盒登录后会使用spring security 进行加密,主要是结合用户名和密码来加密,生成一个唯

利用Node.js实现模拟Session验证的登陆

1.身份验证和用户登陆 在一般的Web应用上,如果要实现用户登陆,最常用,也是最简单的方法就是使用Session,基本的思路是在Session中保留一些用户身份信息,然后每次在Session中取,如果信息不正确或不存在,那么身份验证失败,正确则成功. Session和Cookie是两个很相似的东西,都是字符串,只不过Session是保存在服务器上的,而Cookie是保存在本地的,所以Cookie是不能用作身份验证的.Session故名思议,肯定和客户端与服务器间建立的会话相关,Session的工

利用node.js搭建简单web服务器的方法教程

前言 使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块.路径解析模块.以及301重定向问题,下面我们就简单讲一下如何来搭建一个简单的Web服务器. 早先不使用web服务器的情况下想要在浏览器端访问本地资源,可以利用firefox浏览器,其可以自己启动一个小型web服务器. 为了让刚接触node的人也能大体看懂,本文的代码我将尽量简化. 准备 首先,

利用node js快速模拟Web API

Web API即使通过网络进行调用的API接口,与具体的编程语言无关.现在常见的是通过标准的HTTP GET/POST请求,从服务器获取响应的资源或服务,服务器返回调用的结果内容,一般为xml格式或者json格式的数据(现在使用json的更多). 在开发App的时候,一般原型设计好(如使用just in mind之类的工具)之后,我们会设计出与服务器交互的接口文档.一般情况下,App的开发进度(尤其原型)要快于服务器的开发进度.在App静态原型开发完到服务器实现所有的交互接口这段期间内,我们当然

利用node.js写一个后台服务器---express框架的搭建及使用

一.node.js+express框架的服务项目搭建 step1:创建一个项目目录:myApp step2:命令行进入该目录,执行: npm init 在命令执行过程中,会让你设置一个项目的入口文件(entry point),可以随意设置例如:index.js. 执行完毕后,会在项目中创建一个package.json的文件,这个文件就是用来管理项目中今后需要安装的一些模块或依赖. step3:安装express插件: npm install express --save step4:expres

5 个快速的 Node.js 应用性能提示

本系列文章涵盖许多基础性内容:它给出了应用程序性能管理(APM)的总体概述:指明了实现一个 APM 策略的主要挑战:提出了衡量,评估一个企业级 Node.js 应用程序运行状况的最重要的 5 条指标:并提出了通过 AppDynamics 方式构建一个 APM 解决方案.在文章的最后部分,还提出了一些提示和技巧类以帮助您实现最佳的 APM 策略.具体地说,本文讨论了以下主题: 业务交易优化 快照调优 阈值调优 层级管理 上下文信息捕获 1.业务交易优化 本文章系列里,我会不断重复强调的就是监控方案

[转载]PayPal为什么从Java迁移到Node.js,性能提高一倍,文件代码减少44%

http://ourjs.com/detail/52a914f0127c763203000008 大家都知道PayPal是另一家迁移到Node.js平台的大型公司,Jeff Harrell的这篇博文 Node.js at PayPal  解释了为什么从Java迁移出来的原因: 开发效率提高一倍(2个人用更少的时间干了5个人的活), 性能提高一倍, 代码量减少33%, 文件减少40%: (小编: 个人认为深层次原因是Java正在越来越走向封闭,而且变得越来越复杂而且oracle正在对Java收费,

准备:新V8即将到来,Node.js的性能正在改变

V8的Turbofan的性能特点将如何对我们优化的方式产生影响 审阅:来自V8团队的Franziska Hinkelmann和Benedikt Meurer. **更新:Node.js 8.3.0已经发布了V8 6.0和Turbofan. Node.js依靠V8 JavaScript引擎来运行代码,其语言本身也是我们熟悉和喜爱的.V8 JavaScript引擎是Google为Chrome浏览器编写的JavaScript虚拟机.从一开始,V8的一个主要目标是让JavaScript运行地更快,或者至

Node.js 应用性能优化的五个技巧

在这个由软件定义的世界里,企业往往是通过 Web 应用和移动应用程序来提供他们大部分的服务.所以对企业来说,一个非常重要的任务就是要确保用户拥有出色的使用体验.Node.js 正迅速成为时下最流行的平台之一,它被用于方便地搭建响应速度快.易于扩展的网络应用和移动应用程序.通过下图也可以看出,Node.js 正在成为新的主流. 众所周知,Node.js 是单线程服务器,新事件会触发代码的执行,进行一系列 I/O 操作,并在完成后回调.对于 I/O 密集型的应用,例如 Web 和移动应用程序,这种事