词条信息

admin
admin
超级管理员
词条创建者 发短消息   

相关词条

热门词条

更多>>
什么是端口?到底是做什么的呢?
端口一般指两种,一种是硬件比如路由器或者交换机的插网线的端口,一种是软件的逻辑的概念,比如http的80端口!...
7种进阶方法让你快速测试端口连通性
Ping是Windows、Linux和Unix系统下的一个检查网络连通性的命令工具,对于大部分互联网用户来说很...
电脑开机,总需要按F1,是什么原因造成的?
一.主板掉电这个说法是行业内的叫法了,一般是主板的CMOS电池没电了导致的。也是最常见的一种提示你按F1的提示...
社保降费对个人有什么影响?
下调城镇职工基本养老保险单位缴费比例是政府给企业发的一个大红包,特别是对于企业来说是一个利好,但是对个人来说有...
车辆“出险”对下年保费的影响,到底有多大?
【出险对交强险的影响】【出险对商业险的影响】车辆“出险”对下年保费的影响,到底有多大?这里有必要先提下车险第三...

精选图集

更多>>
简易百科旧版 >>所属分类 >> 前端开发   

提高Node.js应用吞吐量的几个小技巧

标签: Node.js 吞吐量

顶[0] 发表评论(0) 编辑词条

欢迎关注IMWeb!本文作者——Jorge Bay是Apache Cassandra项目Node.js以及C#客户端驱动的核心工程师,同时还是DataStax的DSE。


他乐于解决问题与提供服务端解决方案,Jorge拥有超过15年的专业软件开发经验,他为Apache Cassandra实现的Node.js客户端驱动同样也是DataStax官方驱动的基础


当我们希望去优化某个包含了IO功能的应用性能时,我们需要对于应用耗费的CPU周期以及那些妨碍到应用并行化执行的因素了如指掌。本文则是分享我在提升Apache Cassandra项目中的DataStax Node.js 驱动时的一些思考与总结出的导致应用吞吐量降级的关键因素。


目录

内容提点编辑本段回目录

1.尽可能地使用聚合IO操作,以批量写的方式来最小化系统调用的次数。

2.需要将发布的开销考虑进内,清除应用中不同的定时器

3.CPU分析器能够给你提高一些有用信息,但是并不能完整地反馈整个流程。

4.谨慎使用ECMA高级语法,特别是你还未使用最新的Java引擎或者类似于Babel这样的转换器的时候。

5.要洞察你的依赖树的组成并且对你使用的依赖进行适当的性能评测


背景编辑本段回目录


Node.js使用的标准Java引擎V8会将Java代码编译为机器码然后以本地代码的方式运行。V8引擎使用了如下三个组件来同时保证较低的启动时间与最佳性能表现:

1.能够快速将Java代码编译为机器码的通用编译器

2.能够自动追踪应用中代码执行时间并且决定应该优化哪些代码模块的运行时分析器

3.能够自动优化被分析器标注的待优化代码的优化编译器;并且如果操作被认为是过优化,该编译器还能自动地进行逆优化操作


尽管优化编译器能够保证最佳的性能表现,但是它并不会对所有的代码进行优化,特别是那些不合适的代码编写模式。你可以参考来自Google Chrome DevTools团队的建议来了解哪些代码模式是V8拒绝优化的,典型的包括:

1.包含try-catch语句的函数

2.使用arguments对象对函数参数进行重新赋值


虽然优化编译器能够显著提升代码允许速度,但是对于典型的IO密集型的应用,大部分的性能优化还是依赖于指令重排以及避免高占用的调用来提高每秒的操作执行数目;这也会是我们在接下来的章节中需要讨论的部分。


测试基准

为了能够更好地发现那些可以惠及最多用户的优化技巧,我们需要模拟真实用户场景,根据常用任务执行的工作量来定义测试基准

首先我们需要测试API入口点的吞吐量与时延;除此之外如果希望获取更多的信息,你也可以选择对于内部调用方法进行性能评测。推荐使用 process.hrtime() 来获取实时解析与执行时长。

虽然可能会对项目开发造成些许不便,但我还是建议尽可能早地在开发周期中引入性能评测。可以选择先从一些方法调用进行吞吐量测试,然后再慢慢地增加譬如时延分布这些相对复杂的测试。


CPU 分析

目前有多种CPU分析器可供我们使用,其中Node.js本身提供的开箱即用的CPU分析器已经能应付大部分的使用场景。内建的Node.js分析器源于V8内置的分析器,它能够以固定地频率对栈信息进行采样;你可以在运行node命令时使用--prof参数来创建V8标记文件。

然后你可以对分析结果进行聚合转化处理,通过使用--prof-process参数将其转化为可读性更好的文本:

在编辑器中打开经过处理的记录文件,你可以看到整个记录被划分为了部分,首先我们来看下 Summary 部分,其格式如下所示:



上面的值分别代表了在Java/C++代码以及垃圾收集器中的采样频次,其会随着分析代码的不同而变化。然后你可以根据需要分别查看具体的子部分(譬如[Java], [C++], ...)来了解具体的采样信息。


除此之外,分析文件中还包含一个叫做[Bottom up (heavy) profile]的非常有用的部分,它以树形结构展示了买个函数的调用者,其基本格式如下:



上面的百分比代表该层调用者占目标函数所有调用者数目的比重,而函数之前的星号意味着该函数是经过优化处理的,而波浪号代表该函数是未经过优化的。在上面的例子中,function199%的调用是由 function2 发起的,而 function3 占据了 function2 100%的调用占比。CPU 分析结果与 火焰图 是非常有用的分析栈占用与CPU耗时的工具。不过需要注意的是,这些分析结果并不意味着全部,大量的异步IO操作会让分析变得不那么容易。


系统调用编辑本段回目录

Node.js利用Libuv提供的平台无关的接口来实现非阻塞型IO,应用程序中所有的IO操作(sockets, 文件系统, ...)都会被转化为系统调用。而调度这些系统调用会耗费大量的时间,因此我们需要尽可能地聚合IO操作,以批量写的方式来最小化系统调用的次数。


具体而言,我们应该将Socket或者文件流放入到缓冲中然后一次性处理而不是对每个操作进行单独处理。你可以使用写队列来管理你的所有写操作,常用的写队列的实现逻辑如下:


1.当我们需要进行写操作并且在某个处理窗口期内:将该缓冲区添加到待写列表中


2.连接所有的缓冲区并且一次性的写入到目标管道中。


你可以基于总的缓冲区长度或者第一个元素进入队列的时间来定义窗口尺寸,不过在定义窗口尺寸时我们需要权衡考虑单个写操作的时延与整体写操作的时延,不能厚此薄彼。你也需要同时考虑能够聚合的写操作的最大数目以及单个写请求的开销。你可能会以千字节为单位决定一个写队列的上限,我们的经验发现8千字节左右是个不错的临界点


当然根据你应用的具体场景这个值肯定会有变化,总结而言,当我们采用了批量写之后系统调用的数目大大降低了,最终提升了应用的整体吞吐量。


Node.js 定时器编辑本段回目录


Node.js中的定时器与window中的定时器具有相同的API,可以很方便地实现简单的调度操作;在整个生态系统中有很广泛的应用,因此我们的应用中可能充斥着大量的延时调用。类似于其他 基于散列的轮转调度器 ,Node.js使用散列表与链表来维护定时器实例。不过有别于其他的轮转调度器,Node.js并没有维持固定长度的散列表,而是根据触发时间对定时器建立索引。添加新的定时器实例时,如果Node.js发现已经存在了相同的键值(有相同触发事件的定时器),那么会以O(1)复杂度完成添加操作。


如果还不存在该键值,则会创建新的桶然后将定时器添加到该桶中。需要铭记于心的是,我们应该尽可能地重用已存在的定时器存放桶,避免移除整个桶然后再创建一个新的这种耗时的操作。举例而言,如果你使用滑动延时,那么应该在使用 clearTimeout() 移除定时器之前使用 setTimeout() 创建新的定时器。我们对于心跳包的处理中在移除上一个定时器之前会先确定下以O(1)复杂度调度空闲的定时器。


Ecma 语言特性编辑本段回目录


当我们着眼于整体的性能保障时,我们需要避免使用部分Ecma中的高级语言特性,典型的譬如: Function.prototype.bind()Object.defineProperty()以及 Object.defineProperties()。我们可以在Java引擎的实现描述或者问题中发现这些特性的性能缺陷所在,譬如 Improvement in Promise performance in V8 5.3 以及 Function.prototype.bind performance in V8 5.4 。另外你也需要谨慎使用ES2015或者ESNext中的新的语言特性,它们相较于ECMA 5中的语法会慢很多。 six-speed 项目网站 就追踪了这些语言特性在不同的Java引擎上的性能表现,如果你尚未发现某些特性的性能评测你也可以自己进行一些测试。


V8 团队也一直致力于提高新的语言特性的性能表现,最终使其与底层实现保持一致。我们可以在 性能规划 中随时了解他们对于ES2015性能优化的工作进展,这里他们会收集使用者对于提升点的建议并且发布新的设计文档来阐述他们的解决方案。


考虑到V8的提升可能需要较长的时间才能合并入LTS版本的Node.js: 根据 LTS规划 只有在Node.js大版本迭代时才会合并进最新的V8版本。你可能要等待6-12月才能发现新的V8引擎被合并进入Node.js的运行环境中,而目前Node.js的新的发布版本只会包含 V8引擎中的部分修复 。


依赖编辑本段回目录

Node.js 运行时为我们提供了完整的IO操作库,但是ECMA语法标准则仅提供了寥寥无几的内建数据类型,很多时候我们不得不依赖第三方的库来进行某些基本任务。没有人能保证这些第三方的库可以准确高效地工作,即使那些流行的明星模块也可能存在问题。


Node.js的生态系统是如此的繁荣茂盛,可能很多依赖模块中只包含几个你自己很方便就能实现的方法。我们需要在重复造轮子的代价与依赖带来的性能不可控之间做一个权衡。我们团队会尽可能地避免引入新的依赖,并且对所有的依赖持保守态度。不过对于 bluebird 这样本身发布了可信赖的性能评测的库我们是很欢迎的。


我们的项目中使用 async 来处理异步操作,在代码库中广泛地使用了 async.series() , async.waterfall() 以及 async.whilst()。确实我们很难说这样连接了多个层次的异步处理库就是性能受损的罪魁祸首,幸好有很多其他开发者定位了其中存在的问题。我们也可以选择类似于 neo-async 这样的替代库,它的运行效率明显提高并且也有公开的性能评测结果。


总结编辑本段回目录

本文中提及的优化技巧有的属于常识,有的则是涉及到Node.js生态系统以及Java核心引擎的实现细节与工作原理。在我们开发的客户端驱动中,通过引入这些优化手段我们达成了两倍的吞吐量的提升。


考虑到我们的Node.js应用以单线程方式运行,我们应用占据CPU的时间片与指令的排布顺序会大大影响整体的吞吐量与高平行的实现程度。


本文翻译自 InfoQ 英文站的 node-micro-optimizations-java 一文,从属于笔者的Web 前端入门与工程实践。



 

 

附件列表


按字母顺序浏览:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

→我们致力于为广大网民解决所遇到的各种电脑技术问题
 如果您认为本词条还有待完善,请 编辑词条

上一篇前端开发调试的神器3件套
下一篇用node写个爬虫?

0
1. 本站部分内容来自互联网,如有任何版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
2. 本站内容仅供参考,如果您需要解决具体问题,建议您咨询相关领域专业人士。
3. 如果您没有找到需要的百科词条,您可以到百科问答提问或创建词条,等待高手解答。

关于本词条的提问

查看全部/我要提问>>