目录
1 背景
之前有一个项目,面临性能上的瓶颈:这个项目是一个高频IO的跑数任务型项目,内存占用巨大,cpu也有一定程度的耗损,这个项目是基于JAVA编写的。在遇到性能问题之后,也采取了一定的拯救措施:mat分析内存泄漏、代码层面进行无用对象置空等操作,但是收效甚微,这个时候想起要能否使用node来重写java的这一块逻辑呢。
注:在此之前,笔者已经有比较丰富的node服务端的经验了。
2 业界综述
下面的论述,仅借部分网友的实验,非最终结论,仅供参考
下面借助部分网友提供的试验数据来说明:
测试模拟器源码:https://github.com/johnrjenson/javavsnode
为了证明Node确实可以超越Java,介绍了4种场景,在这些场景中通过使用模拟器来测试这两种技术的性能:
注释:
本测试中,为Java实现模拟阻塞IO。有些人可能会认为使用阻塞IO并不公平地比较Java的性能,但在本测试中使用阻塞IO的原因如下:①Java的JDBC规范仍然是一个阻塞规范,这意味,每当有人使用标准JDBC驱动程序连接到关系数据库时,都必须阻塞。② Apache Tomcat 8.5在2016年6月才完成了第一个非阻塞Servlet规范,这意味着绝大多数生产Java应用程序在执行IO时仍会阻塞。因此,由目前大多数组织以阻塞的方式使用Java,所以在本次模拟中,阻塞也是最具有代表性的Java。
场景1:纯计算速度
在第一个场景中,选择一个纯计算工作负载来证明Java的计算优势。分别在有和没有并发的情况下进行测试模拟,工作负载情况:
1a:①无并发;②计算500个质数;③执行0个阻塞IO调用。
1b:①有并发;②计算500个质数;③执行0个阻塞IO调用。
测试结果
1a:纯计算任务在没有使用并发时,Java速度是Node的2.1倍左右;
1b:一旦将并发加入到任务中,Java领先Node的速度降低到1.8倍。
场景2:大量IO操作
第二种模拟中,不做任何计算工作,只执行IO任务。分别在有和没有并发的情况下进行测试模拟,工作负载情况如下:
2a:①无并发;②计算0个质数;③执行4个阻塞IO调用;④处理一个5MB文件。
2b:①有并发;②计算0个质数;③执行4个阻塞IO调用;④处理一个5MB文件。
测试结果
2a:没有并发时,Node的速度是Java的3.4倍;
2b:有并发时,Node的速度增加到Java的8.5倍。添加并发后,Node与Java之间的差异更加明显。
场景3:均衡的工作负载
此场景与典型Web应用程序的请求工作负载最接近,模拟中同时执行IO和计算任务,均衡工作负载。同样,也是在有和没有并发的情况下分别模拟。
工作负载情况:
3a:①无并发;②计算500个质数;③执行4个阻塞IO调用;④处理一个5MB文件。
3b:①有并发;②计算500个质数;③执行4个阻塞IO调用;④处理一个5MB文件。
测试结果
3a:没有并发时,Node的速度是Java的1.8倍;
3b:有并发时,Node的速度增加到Java的4.9倍。
场景4:长时间运行的查询
工作负载情况:①计算500个质数;②发出一个很长的阻塞IO调用——10s响应时间;③处理一个5MB文件
测试结果
Java每秒6个请求;Node每秒64个请求,是Java的10倍
观察结果能够发现,执行长时间运行的查询,Java运行的性能大大受影响,但相同场景下,Node的性能几乎没有降低。也就是说,Node完胜Java。
结论
在典型Web应用程序的真实场景中,Node确实是比Java运行速度更快、更具可伸缩性。对于Web应用程序开发来说,Node的性能是难以比拟的。由于Web应用程序大部分时间都在做IO,并且需要高并发,相比之下,Node显然是最终的赢家。
3 成功案例
领域 | 成功案例名称 | 我的案例 |
工具类应用 | 过去依赖java或者其他语言构建的前端工具类应用,纷纷被前端工程用node重写,比如vscode | 前端工具类直接搬到node服务端使用 |
游戏开发领域 | 网易开源了Pomelo框架,是基于node的高性能、分布式游戏服务器框架 | 使用puppeteerr做自动化测试,使用puppteer做服务端截图等浏览器动作 |
云计算平台提供node支持 | 阿里云和百度在云服务器上提供node应用托管服务 | 小试:云函数使用 |
并行I/O利用 | Ebay(海淘) 阿里巴巴(云原生)
| |
高性能I/O用于实时应用 | Voxer将node用于实时语言上 腾讯朋友网将node应用在长连接中 花瓣网、蘑菇街使用socket.io实现实时通知功能 | 实时跑数大屏 小试:在线打字PK游戏 |
新型物联网开发框架 | Iot.js, ducktape, linkit等 | |
其他 | 做实时通讯/爬虫/开发各种类型的动态网站(企业网站/商城/聊天室/直播/大量用户表单收集/微信公众平台等) | |
框架支持 | 第三方应用提供node的api支持,比如es,提供node语法和kibana语法平行使用 | node+es实现搜索引擎 |
4 设计考虑
4.1 可维护性
开发效率,和团队协调,node都比较高。
开发效率:Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发,Node打破了过去JavaScript只能在浏览器中运行的局面。前后端编程环境统一,可以大大降低开发成本。应用在项目开发中,开发周期短、开发成本低、学习成本低
维度 | 表现 | 备注 |
开发周期 | 短! | 关注业务,极简代码风格 |
开发成本 | 低! | 0s热更新,随处编码,随处调试,前端代码可移植,更简单的api使用 |
学习成本 | 低! | 在有经验的node开发人员帮助下,前端人员2天转型,后端人员1天转型。 |
模块库 | 全、多、新 | 19年数据:开源NPM存储库(超过600000个模块),可手写模块、上传模块 |
4.2 问题和解决
Node.js优点很多,但是缺点也不少,但是都有对应的解决方案。
缺点 | 解决方案 |
异步API编码习惯不适应 | async await配合可以解决 |
回调地域 | async await配合可以解决 |
松散类型边界、编译时检查、运行时检查 | 模块化编程可以快速定位到问题 |
Node只支持单核CPU,不能充分利用CPU资源。 | 如果需要的话,可以分布多台低配主机(可以更省成本) |
一旦代码某个环节崩溃,将会导致整个系统都崩溃 | 全局链条结构编码,异常捕捉 |
5 方案描述
java的技术方案已经非常成熟了,node的技术方案其实也比较成熟了。目前主要就是基于koa来打造服务端。
应用类型 | 表现 | 备注 | ||||||||||||||||||||||||||||||||
WEB应用 | Node关于web开发的框架,从express进阶到koa(可以理解成是express2.0版本)。 以koa为例子: 目录结构:
涉及组件(常用):
| 上线文件体积小,基本在1M以内。 可以实现自动化上线。 后台运行。 0S停机重载 Webstorm/hbuilderx/idea/sublime/记事本等都可以作为开发工具 | ||||||||||||||||||||||||||||||||
桌面应用 | - | 有需要再分享 |
6 我的实践
在论证这个方案之前,笔者自己其实也做了一个长达3个月的实验。
6.1 应用简介
跑数服务 + 跑数看板,就是一个典型应用:
从上线(21.2.17)至今(21.5.20),已经稳定运行了3个月了。
这个应用特点:高频数据操作,数据量大,需要监控数据同步进步等。
6.2 应用技术选型
使用node来完成服务开发。
跑数程序特点:局部数据个性化难把握,需要结合实际机器情况以及上游服务能力,调整跑数进程和请求频率等。
对接外部程序经历:业务流程变动极大,收到的信息和实际的信息不对等等。
技术选型 | ||
app(客户端) | ||
技术栈 | 版本 | 备注 |
Vue | V2.6 | |
Element-ui | V2.13.1 | |
Axios | V0.20 | |
Js-md5 | V0.7.3 | |
Vue-count-to | V1.0.13 | |
Vue-router | V3.2.0 | |
Socket.io-client | V2.1.1 | 实时通信 |
Vue-socket.io | V3.0.7 | 实时通信 |
service(服务端) | ||
Koa | V2.13.0 | |
Koa-bodyparser | V4.3.0 | |
Koa-cors | V0.0.16 | |
Koa-router | V9.4.0 | |
Koa-send | V5.0.1 | |
Koa-sslify | V4.0.3 | |
Koa-static | V5.0.0 | |
Md5-node | V1.0.1 | |
Mysql2 | V2.1.0 | |
Node-schedule | V1.3.2 | |
Node-xlsx | V0.15.0 | |
Sequelize | V6.3.5 | |
Socket.io | V2.3.0 | |
Superagent | V6.1.0 | |
Urldecode | V1.0.1 | |
Urlencode | V1.1.0 | |
Uuid | V8.3.0 |
6.3 应用架构
6.3.1 系统架构
6.3.2 应用运行状态
应用部署使用pm2进程守护模块来部署(pm2 是一个带有负载均衡功能的Node应用的进程管理器)
C:\Users\Administrator>pm2 show 17
Describing process with id 17 - name robot-service
┌───────────────────┬────────────────────────────────────────────
│ status │ online │
│ name │ service │
│ namespace │ default │
│ version │ 1.0.0 │
│ restarts │ 34 │
│ uptime │ 2M │
│ script path │ E:\service-task\service.js │
│ script args │ N/A │
│ error log path │ E:\service-task\logs\pm2_error.log │
│ out log path │ E:\service-task\logs\pm2_out.log │
│ pid path │ C:\Users\Administrator\.pm2\pids\robot-service-17.pid │
│ interpreter │ node │
│ interpreter args │ N/A │
│ script id │ 17 │
│ exec cwd │ E:service-task │
│ exec mode │ fork_mode │
│ node.js version │ 10.15.3 │
│ node env │ N/A │
│ watch & reload │ ✘ │
│ unstable restarts │ 0 │
│ created at │ 2021-05-20T214:41:13.979Z │
└───────────────────┴────────────────────────────────────────────
Actions available
┌────────────────────────┐
│ km:heapdump │
│ km:cpu:profiling:start │
│ km:cpu:profiling:stop │
│ km:heap:sampling:start │
│ km:heap:sampling:stop │
└────────────────────────┘
Trigger via: pm2 trigger robot-service <action_name>
Code metrics value
┌────────────────────────┬──────────────┐
│ Heap Size │ 48.21 MiB │
│ Heap Usage │ 63.36 % │
│ Used Heap Size │ 30.55 MiB │
│ Active requests │ 0 │
│ Active handles │ 9 │
│ Event Loop Latency │ 0.06 ms │
│ Event Loop Latency p95 │ 1.05 ms │
│ HTTPS Mean Latency │ 2 ms │
│ HTTPS P95 Latency │ 4 ms │
│ HTTPS │ 0.06 req/min │
└────────────────────────┴──────────────┘
6.3.3 关键状态参数
根据上面信息,可以发现当前应用下面几个信息:
参数名称 | 参数解释 | 备注 |
内存占用值 | 80M | 在开发调试阶段,监控过内存浮动过程,无明显变动 |
7 笔者想说
Java和Node这两个本来就没有可比性。
Java是一门语言,Node是一个运行环境。
如果非要说,哪个更好,这个命题本来就是伪命题。
注:伪命题是指不真实的命题。所谓不真实,有两种情况:其一是不符合客观事实;其二是不符合一般事理和科学道理。(看似正确却缺乏实际意义的观点)
不管Node还是Java,都有适用他们的场景。在合适的场景里头,选型正确的技术,可以简化问题、拉近目标。
Java作为一个已经有20多年历史的大佬语言,在过去和未来都会持续为解决诸如传统的web开发,cpu计算型应用发挥作用。
Node从2009年诞生至今,一直都饱受争议,但是随着各大产商的重资源投入,以及开源库、开源项目的不断涌现,无论是基于Node的项目、服务还是为Node项目提供的支撑都一直在火热中,从未衰退。从小厂到大厂,都不乏有基于Node项目。
其实Node.js作为后端能实现几乎所有应用,只是我们选择的时候考虑更多的是这个应用选择Node.js是不是最适合的。
个人觉得:技术无业界,能解决问题的技术都是好技术,我不偏向说用Node替换Java,也不会说因为Java有多年的底蕴,就拒绝用新技术解决问题。但是技术更新和迭代迅猛如今天,是否可以看看为什么会有这么多新的高级语言的出现,以及被追捧的技术,是否真的可以加速我们日常的问题的解决,如果是,为何不试试?万一就成了呢?至于网上的言论,褒贬不一,我觉得,还是亲力亲为,用过了,是否适合,才知道一二!
8 名词解释
8.1 cpu密集型任务
指进程绝大部份任务依靠cpu的计算能力完成,典型的如同科学计算,数值模拟等程序。
8.2 io密集型任务
指绝大部分任务就是在读入,输出数据,典型的例如web后端程序,主要就是在根据url请求找到对应的资源并输出。mysql的大量读写属于io密集。