我为什么反对用Node
随着无线端的快速普及,前后端分离技术走上前台,而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还有其它一些优点如单线程、RESTful API等,总体来说Node是为轻量级的分布式的实时数据服务这类应用提供运行容器而设计的,这类应用很容易想到微博、非死book这类典型场景,需要非常实时化、个性化、高并发的数据服务。
为什么火了
今年由于无线终端的兴起,后端要提供基于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如何解决客户端更新和服务端的快速迭代,这个问题是当前正在着力解决的,目前为止有两种思路:一种是客户端用同一种技术 开发,然后通过工具编译技术把它编译成不同平台上能够执行的代码,如当前的Reactor.js;另一种思路是将客户端中经常需要更新的模块做成动态推送 的,用模板+数据的方式,在不同的客户端平台上实现一个小的解析引擎来实现快速个性化的定制,目前手淘主要采用后面一种实现方式,当然前一种也正在尝试。
那么再说回来,基于前面的这些推断,多终端和服务端交互主要是数据+模板的方式为主,那么服务端提供格式化的数据将成为必然选项。所以涉及到的问 题就是服务端既要提供格式化的数据(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成为全栈工程师,但是是否同一个人愿意用两种不同的语 言完成同一个任务呢,正常来说,如果我能用同一个方式完成全部工作,我为什么要把一个任务分成两种不同的方式来完成,这显然也不太合理。所以部分前端同学 通过要求后端同学也学习JavaScript,来达到让后端同学来使用Node这显然有点打错算盘,不过后端同学学习JavaScript可以实现页面的 JS交互逻辑倒是可以替代前端的工作。
怎么办
基于前面的分析,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模板。这种解决方案达到几个目的:
- 我们的后端系统完成的组件化改造,PC和无线逻辑统一起来了;
- 将渲染层独立,渲染层后业务量逻辑层通过JSON数据对象交互;
- 前端开发同学完全掌握了XTPL+JS逻辑,有更多的掌控力;
- 前端开发页面不需要依赖后端的Java系统,调试页面可以在Node中完成;
- 系统的架构并没有增加一层,运维上也没有引入新的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可以联系到我。