GTLC全球技术领导力峰会·上海站,首批讲师正式上线! 了解详情
写点什么

我为什么反对用 Node

2015 年 8 月 30 日

随着无线端的快速普及,前后端分离技术走上前台,而 Node 由于它的一些特性被工程师快速接受尤其是前端工程师,所以产生了很多 Node 是否会引起新的技术变革的讨论。我本人是淘系的一个 Web 开发人员,基本上经历了淘系关于 Node 和 Java 技术选型讨论的过程,所以今天我给大家推演一下在像淘系这个环境下 Node 能否会成为主流的 Web 开发技术,当然后面也给出了我认为比较适合的场景。

Node 火了

在百度中搜索 Node 可以得到 105w 个结果,图书出版方面 13 年 3 月到 15 年 6 月 2 年时间有近 20 种相关的 Node 书出版,实践方面国外公司 PayPal、LinkedIn、groupon 也都在使用,国内大公司阿里、腾讯、百度也都有实践项目在尝试。这让我想起了当初 Nosql 新出来是一样的场景,大家都一窝蜂的涌上去拥抱新技术,获取新技术带来的红利。

Node 很火的另一个推手是当前的无线技术流行,很多应用从传统的 PC 开发要转到无线等多端,这种情况下渲染层和逻辑层的分离变得重要起来,而 Node 刚好可以很好的渲染前端页面,所以我们的开发同学不遗余力的在推广 Node 技术。

Node 能够火起来最重要的原因还是它的确给我们的开发带来了很多好处:

  • 基于事件驱动
  • 无阻塞 I/O

Node 还有其它一些优点如单线程,总体来说 Node 是为轻量级的分布式的实时数据服务这类应用提供运行容器而设计的,这类应用很容易想到微博、Facebook 这类典型场景,需要非常实时化、个性化、高并发的数据服务。

为什么火了

今年由于无线终端的兴起,后端要提供基于 JSON API 的数据接口非常普遍。目前来看公司还存在两种形态:一个是无线作为新系统独立存在于传统 PC 系统;另外一种是将无线系统合并到老的 PC 系统,在一个系统里同时支持多端服务。长期来看无线和 PC 系统的合并是必然,业务上以无线为主,PC 仅仅作为一个终端而已,不可能存在无线和 PC 两套业务逻辑。

那基于无线和 PC 业务合并,由一个系统提供多终端、多语言适配的角度来看,Node 能否在其中扮演传统服务端 MVC 中的 V 角色?

要回答这个问题,我们再设想一下,在多端情形下,需要怎样的交互:PC、手机端、Pad、TV、Car、Watch 等其它移动终端。

是 Native 还是 H5

当前移动端主要还是以 Native 实现为主,从用户体验角度来考虑 Native 的实现要比 H5 更流畅,同时 Native 还可以基于本地做很多在浏览器里不能做的优化,如大数据的存储、可以定制的通信协议、更方便的保持长连接以及更容易实现的实时消息推送。

当然 H5 也有其无法比拟的优势,客户端更轻量级,服务端发布更迅速,不需要用户升级版本等。长期来看移动端能否会向早期 PC 那样也从富客户端转向浏览器呢?

我的判断是未必,有几个因素,首先 Native 实现性能优势相比 H5 会好很多,当前移动端都在追求极致体验的时代,无疑 APP 会比 H5 有很多的优势;其次,移动端屏幕较小,基于网页的交互和 APP 相比还有很多限制。最重要的是不同的商家是主推带有品牌标识的 APP 还是向统一的浏览器靠拢,从目前的趋势看,APP 会是手机端上争夺的重点。所以推测直接基于手机端的浏览器的应用不会成为主流前端。

如何实现快速迭代

基于 APP 的 Native 如何解决客户端更新和服务端的快速迭代,这个问题是当前正在着力解决的,目前为止有两种思路:一种是客户端用同一种技术开发,然后通过工具编译技术把它编译成不同平台上能够执行的代码,如当前的 React Native;另一种思路是将客户端中经常需要更新的模块做成动态推送的,用模板 + 数据的方式,在不同的客户端平台上实现一个小的解析引擎来实现快速个性化的定制,目前手淘主要采用后面一种实现方式,当然前一种也正在尝试。

那么再说回来,基于前面的这些推断,多终端和服务端交互主要是数据 + 模板的方式为主,那么服务端提供格式化的数据将成为必然选项。所以涉及到的问题就是服务端既要提供格式化的数据(Http JSON 数据),又要支持传统的 PC 的方式:基于 JSON 数据渲染出 HTML 页面,所以很容易想到将渲染层独立出来用 Node 完成。

真的靠谱吗

既然 Node 可以带来这么多好处,那么我们不妨就继续向下推演,看是否真的很靠谱?下面看下 Node 在我们的实际的开发环境中如何使用,在引入 Node 之前我们有必要先介绍下当前 Web 服务端架构:

(点击放大图像)

图1. 传统的web 架构

在当前这种架构下Node 怎么融入进去呢?最保守的一种方案是将当前的Java Web 中的VIEW 层从MVC 中独立出来,交给Node 来完成,Java Web 只提供基于JSON 数据接口给Node 调用,架构图变成了如下的形式:

(点击放大图像)

图2. Node 作为渲染层加入到传统架构中

很明显的差别是在我们当前的访问路径上多增加了一个Web 代理层,而这一层和当前的Web 服务器层怎么看都有点别扭,两者同时存在始终觉得有一个是多余的,那么Node 能否替代掉Nginx 成为Web 代理服务器呢?理论上是完全没问题的,就像我们用PHP 来代替Java Web 开发一样,不过你放到具体的公司运维体系中,你会发现目前在Nginx 上的防攻击、限流、数据埋点、热点cache 等模块都要在Node 上重新开发一遍,最重要的是用Node 取代Nginx 并不能带来额外的好处,如果你说Node 可以渲染页面,那么Nginx 的开发会和你讨论Nginx lua 模块和Node 哪个更合适,所以用Node 取代Nginx 作为代理服务器也不太现实。

那么Node 能否取代前端台的Web 系统,成为主流的MVC 框架呢?

未必

Node 和 Java Web 一样可以提供 MVC 管理功能,一个系统中同时存在两套 MVC 框架显然不合理,那么如果用 Node 来替换 Java Web 的话,服务端的架构变成如下的:

(点击放大图像)

图3. Node 替代Java Web

从技术实现上这种架构没什么大问题,就是用Node 上的MVC 框架如express 来替代Java Web 中Webx,也就是用JavaScript 替换Java,以及整个运行容器和中间件都要替换,那么是否真的带来那么大的好处呢?

我们从语言特性、开发效率和成本因素三个方面来看,Node 作为后来者能否比我们现在的Java 更优秀。

语言特性

JavaScript 作为 Node 上运行的语言和 Java 相比,优缺点很明显。JavaScript 语法简单,很容易编写基于事件的驱动的实现。但是 JavaScript 基于面向对象的描述能力偏弱,不像 Java 是真正的面向对象语言 ,同时 JavaScript 的对数据类型定义也比较单一,要么是数值类型要么是字符类型。很明显 Java 更擅长构建复杂逻辑的大型应用程序,在这一点上 JavaScript 明显落于下风。

在语言运行效率上,JavaScript 本来是解释执行,而 Java 是编译执行,但是由于 Node 做了优化,所以运行效率差别不大。

开发效率

开发效率可以从语言的复杂度、程序员培养、开发工具包的丰富性以及编码效率几个方面比较。

  • 语言的复杂度。Java 和 JavaScript 从开发角度都不需要关心内存的管理,都是基于虚拟机来管理内存,从并发角度来看:JavaScript 是基于事件触发的,而 Java 是基于线程的,JavaScript 更占优势。另外 JavaScript 是无阻塞 I/O 的,在 I/O 效率上 JavaScript 也更占优势,虽然 Java8 也将更好的支持异步 I/O。
  • 程序员培养。目前 Java 语言仍然是仅次于 c 语言的第二大编程语言,而 JavaScript 排查第 10 位,Java 程序员队伍要比 JavaScript 大很多,很显然 Java 程序员招聘要比 JavaScript 更容易一点。
  • 开发工具包。一个语言的开发效率很多时候要这个语言的支持工具包和组件的丰富性,Java 经过这么多年的发展,工具类库已经非常丰富,几乎你想要的工具类库都能在网上找到。JavaScript 虽然也发展很长时间,但是基于 JavaScript 的工具类库主要集中在前端,能够直接用于 Node 上的仍然很少,当然 Node 的社区非常活跃,可以预见 Node 的工具类库增长也会非常迅速。但是短时间内要达到 Java 的规模尚需时日。
  • 编码效率。Java 语言的运行基于 JVM,但是 Java 的部署效率稍差,而 JavaScript 使测试更加简单,容器重启更快,但是 debug 机制仍然不完善,所以很难做 debug 测试。

成本因素

前面主要是从技术角度考虑,但是如果往 Node 迁移的话,成本也是一个考虑因素。

  • 首先是学习成本。公司大部分是 Java 程序员要往 Node 迁移很明显这个学习成本会非常巨大,即使这个迁移是逐渐的,长期来看仍然是要将一部分 Java 程序员替换成 JavaScript 程序员,不管这个程序员是公司内培养的还是从外部招聘的,我们可以算一下帐,公司招聘一个程序员的成本有多大:一个普通的工程师的年薪假定有 10w 以上,猎头费一般是年薪的 20% 以上,也就是 2w,再加上一个月的实习成本 1w,加在一起约 3w,对于一个有 1w 以上开发的大公司成本可想而知,即使是招聘应届生,由于应届生的培养周期更长,所以学习成本会更高。
  • 其次是环境成本。公司的基础服务产品,如中间件都是基于 Java 开发的,如果要替换成 JavaScript 必然要再开发一套,还有配套的运维工具等,这个成本也可想而知。
  • 最后是维护成本。Java 和 JavaScript 都是基于容器运行,JVM 和 v8 引擎相比,程序员显然对 JVM 更熟悉。另外从排查问题的难易程度来看针对 JVM 的工具显然更完善。

从上面几个方面来看,当前在阿里要让 JavaScript 完全取代 Java 作为后端开发语言,基于 Node 用 JavaScript 实现整个服务层逻辑显然成本会比较高。

换个角度

我们再换一种角度推演一下,假如我们现在的 Web 系统都用 Node 实现,那必然有很多 Java 工程师会做 Node 的开发,因为我们现有的前端工程师人数肯定是支撑不了现有的业务发展的。我们假定一部分 Java 工程师愿意学习 JavaScript 成为全栈工程师,但是是否同一个人愿意用两种不同的语言完成同一个任务呢,正常来说,如果我能用同一个方式完成全部工作,我为什么要把一个任务分成两种不同的方式来完成,这显然也不太合理。

怎么办

基于前面的分析,Node 不管是在现有基础上单独增加一层还是要整个替换 Java Web 层都不太合理,那是否意味着这种前后端分离的思路有没有合理之处?有没有更好的实现呢?

传统的 MVC Web 软件架构将渲染层独立出来交由前端同学控制有其合理性,在当前的多终端开发模式下,将业务逻辑层和前端渲染层分离有利于提升后端的开发效率,后端只需要关注后端的业务逻辑和数据的输出,因为在 Native 开发下服务端只需要输出 JSON 数据,客户端的页面渲染有客户端同学完成。H5 和 PC 需要的 HTML 渲染统一交给前端同学完成有利于前端更好的开发模板,从以往的先画好模板(HTML), 给后端同学转化成相应的模板(如 Velocity 或 JSP),然后再基于复杂的 Java Web 工程下调试页面(前端要独立的运行整个 Java Web 工程还是相当的困难)。而渲染层全部交给前端的话,前端同学和后端只需要约定好数据后,页面完全由前端同学完成,减少了不少交流成本(不过从淘宝的基于 Node 实践来看,整个效率提升还不是那么明显,大部分是把原本是后端的开发工作量转嫁给前端了)。

还有一个重要的理由是前端有了渲染层的控制权,前端的开发体验有了不小的提升,说白了就是前端从以往的配合角色转变成一个 Web 渲染层的 Owner,更加有了主人翁角色,如果再维护 Node 的话,和以往的后端 Java 开发几乎一样了,而这种前端职责的提升恰恰是从后端削弱过来的,所以第一个出来反对的肯定是后端同学,当前在阿里 Node 发展比较缓慢我想也有一点关系吧。

再说回来,我们一直讨论的基于 Node 实现的前端分离方案,可以把他分解一下 Node 技术和前后端分离。很明显前后端分离在当前多终端背景下有其合理性,但是是否一定要用 Node 来实现呢?答案是不一定。当前还有两种方案:

将 Node 层代码抛到 Java 体系上,如下图:

(点击放大图像)

图4. Node 嵌入到Java 中

当前的Java8(Nashorn)已经可以支持JavaScript 的解析,而Avatar.JS 可以将Node 无缝迁移过来,但是经过测试,Nashorn+Avatar.JS 的执行效率太差,有4 到10 倍的性能下降,最好也有一半的下降,这样很难达到工程级别的使用。

另外一种就是仍然在基于Java Web 体系下,而是将渲染层独立出来,渲染层和业务逻辑层仍然通过JSON(或者大对象)数据交互,使得渲染层既可以在Java 上渲染也可以在Node 上执行。如下图所示:

(点击放大图像)

图 5. Java Web 兼容 Node 的模板层

这种方式与前一种的区别是只做渲染引擎的适配,即模板在 Node 和 Java 上都可以解析,而不是把 Node 的整个 MVC 都搬过来,由于 Node 和 Java Web 上都有控制逻辑(即 MVC 中的 C),所以如果 Node 和 Java 中逻辑不一致会导致两边渲染出来的 HTML 不一致,所以需要把 URL 改造成满足 RESTFULL 的格式,尽量把 C 的逻辑简单化。

上图的架构正是目前我们在详情系统上做的一个实践,成功的关键是将 XTPL 的模板从 Node 上无缝迁移到 Java 上,另外就是保持页面的路由尽量简单,这样前端在 Node 上开发的重点只是 XTPL 模板。这种解决方案达到几个目的:

  1. 我们的后端系统完成的组件化改造,PC 和无线逻辑统一起来了;
  2. 将渲染层独立,渲染层后业务量逻辑层通过 JSON 数据对象交互;
  3. 前端开发同学完全掌握了 XTPL+JS 逻辑,有更多的掌控力;
  4. 前端开发页面不需要依赖后端的 Java 系统,调试页面可以在 Node 中完成;
  5. 系统的架构并没有增加一层,运维上也没有引入新的 Node 系统。

没有无往不能的神器

前面介绍了在现有 Java 体系下,要将 Node 替换 Java Web 其实是比较困难的,所以没有一个技术是无往不能的神器,但是是否意味着 Node 就没有应用场景了呢?肯定不是,下面这些情况下 Node 很有用武之地。

创业公司很合适,尤其当创始人之一是熟悉前端的同学的话,用 Node 实现 Web 系统很合适,Node 和 PHP 一样具备快速发布的优势,代码 copy 上去就生效,甚至不需要重启服务器,这一点相比 Java 有很大的优势。当业务逻辑还没有非常复杂时,JavaScript 语言的弱点也没有暴露的那么明显,从系统的维护角度来说,不需要一个工作有两个角色的工程师完成,可以提升开发效率。

重页面交互轻业务逻辑的系统也适合 Node 来开发,说白了如果 Web 系统如有一半以上的 工作量都是需要前端同学来完成的话,那还不如把整个系统都交给前端同学来维护。

如果公司的工程师都是全栈工程师能在不同语言之间自由切换,那么也就没有所为的成本一说了。当然这个仍然要受到公司基础环境的约束,如运维和中间件产品仍然不会同时开发两套。

小结

随着技术的不断进步我们的开发模式也在一直发生着变化,早期的页面渲染和业务逻辑全部集中在一起,如 ASP、PHP、JSP 技术,后来由于业务逻辑不断变得复杂,出现了 MVC 的开发框架,前后端工程师分工也越发清楚。中间也有过前端工程师负责整个渲染层和控制层的实现如 Extjs+Ajax 的开发模式,但是由于整个渲染是在浏览器端完成的受制于客户端渲染性能和搜索引擎的收录页面的硬缺陷很难成为主流。一直到今天前后端开发模式仍然是后端工程师管理 M 和 C,而由前端来实现 V,开发环境和运行环境是一套,所以开发上的耦合导致沟通和调试成本增加。直到 Node 的出现缓解了前后端开发上的耦合,但是这种分离仍然是以增加运行时的维护成本来换取开发时的便利,所以我觉得还不是最佳实践。

本文给出的解决方案也是想兼顾开发时的便利而同时也不增加运行时的维护成本为出发点,当然每种方案都不是完美的,找到适合才是最重要的,随着 Java 中执行 JS 技术的不断成熟,我想开发环境和运行环境的分离肯定不久就将实现,前后端开发的耦合度也就最终解决。

作者简介

许令波,2009 年毕业加入淘宝,目前负责商品详情业务和稳定性相关工作,长期关注性能优化领域,参与了淘宝高访问量 Web 系统主要的优化项目,著有《深入分析 Java Web 技术内幕》一书。 @淘宝君山、 http://www.xulingbo.net 、xulingbo0201@163.com 可以联系到我。


感谢崔康对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015 年 8 月 30 日 19:0347821

评论

发布
暂无评论
发现更多内容

linux入门系列6--软件管理之rpm和yum仓库

黑马腾云

Linux centos 运维 rpm yum

终极学习法,你能学会任何东西--程序员的学习之路

盛安德软件

​JDK1.8新特性(八):还在重复写空指针检查代码?赶紧使用Optional吧!​

xcbeyond

Java 新特性 JDK1.8 Optional

质量门禁:Verigreen开启Git的Commit门禁

陈磊@Criss

37岁程序员被裁,想用6月工资跪舔领导划掉被裁名额,结果蒙了!

程序员生活志

企业信息化怎么构建?

代码制造者

大数据 低代码 企业信息化 零代码 编程开发

linux入门系列7--管道符、重定向、环境变量

黑马腾云

Linux centos 运维 linux命令 管道符

linux入门系列9--用户管理及文件权限控制

黑马腾云

Linux centos centos7 linux运维 linux用户权限

火眼云CEO张陆鹏:A轮融资5000万,解密国内ABM生态首位玩家

ToB行业头条

物联网SIM卡和SIM卡真的不是一回事

华为云开发者社区

人工智能 物联网 华为云 传感器 SIM卡

CHAR与VARCHAR详解

Simon

MySQL

开源,轻松实现RTC与SIP互通

anyRTC开发者

WebRTC 编码 SIP 源码解析

王者荣耀为什么不使用微服务架构?

程序员生活志

英特尔神经拟态芯片Loihi大显身手 帮助轮椅上的儿童实现独立生活

最新动态

区块链技术助力甘肃建食安信息追溯平台 为食品安全“立规矩”

CECBC区块链专委会

食品追溯 食品安全

可能是首个支持部署 Deno 前后端应用的部署工具

binggg

taro GitHub 前端 deno Node

影响音视频延迟的关键因素(二): 采集、前处理、编解码

ZEGO即构

H264 API 3A算法

一位男程序员的英语学习之路

盛安德软件

全票通过!易观开源项目DolphinScheduler进入Apache孵化器

易观大数据

linux入门系列8--shell编程入门

黑马腾云

Linux centos Shell linux命令 linux编程

提高GIT中代码质量的七点优秀实践

程序员生活志

git 经验总结

非IT行业大企程序员讲述MIS系统开发案例

Learun

敏捷开发 企业信息化 企业管理 .net core 「Java 25周年」

【Kafka】消费者客户端小结(java)

guoguo 👻

第11周作业

娄江国

第11周总结

娄江国

如何让我的简历有价值、有亮点

escray

学习 面试 简历 面试现场

区块链技术正向平台化、组件化、集成化演进

CECBC区块链专委会

大数据 区块链技术 科技

INT类型知多少

Simon

MySQL

Devops与敏捷二者能否结合?

禅道项目管理

DevOps Scrum 敏捷开发

python自动生成一整月的排班表

openbytes

Python

网页游戏

小端taro

DNSPod与开源应用专场

DNSPod与开源应用专场

我为什么反对用Node-InfoQ