nodejs 后端作为后端写逻辑真的合适吗

966,690 七月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
我为什么反对用Node
我为什么反对用Node
0&他的粉丝
日. 估计阅读时间:
智能化运维、Serverless、DevOps......2017年有哪些最新运维技术趋势?!
相关厂商内容
相关赞助商
CNUTCon全球运维技术大会,9月10日-9月11日,上海&光大会展中心大酒店,
Node能够火起来最重要的原因还是它的确给我们的开发带来了很多好处:
基于事件驱动
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模板。这种解决方案达到几个目的:
我们的后端系统完成的组件化改造,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技术内幕》一书。 @淘宝君山、、可以联系到我。
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博(,),微信(微信号:)关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入InfoQ读者交流群)。
Author Contacted
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
Re: 一点观点
图中内容改进
Re: 二点观点
Re: 二点观点
Re: 一点观点
没有无往不能的神器
这篇文章解决的问题没踩到点上
Re: 这篇文章解决的问题没踩到点上
感覺就是java碼農對js的發自內心的抵制
建议楼主回到web根本重新学习一遍
感觉看起来很乱,跑题了
web的事web做主
赞同,不反对
前端开发就应该用node.js, 就应该都会node.js
错误还是挺多的
Re: 错误还是挺多的
Re: 错误还是挺多的
不要误人子弟
你的反对并不能阻止Node.js继续火
Re: 二点观点
不存在语言之争, 作者只是站在后端的角度看待node取代view层的可行性问题
Re: 不存在语言之争, 作者只是站在后端的角度看待node取代view层的可行性问题
该用还得用
写这篇文章就要做好被喷的准备
大前端本来就不该有Java什么事
INFOQ什么时候也开始收水文了?
Java fanboy
Re: 感觉看起来很乱,跑题了
Re: 错误还是挺多的
Re: 不存在语言之争, 作者只是站在后端的角度看待node取代view层的可行性问题
我也是醉了
我现在很有理由怀疑阿里的服务器并发能力
nodejs并不是一无是处的
果然写 Java 的人认为什么语言都应该和 Java 一样
重要的是成本,而非技术
Re: 我也是醉了
java拯救世界,java拯救全宇宙了?
Re: Java fanboy
Re: 不存在语言之争, 作者只是站在后端的角度看待node取代view层的可行性问题
还能好好聊天么..
题目偏题了
Re: 不要误人子弟
作者的标题是“我为什么反对用Node”
Re: 作者的标题是“我为什么反对用Node”
标题有误导
Node有它使用的场景
天猫前端已经全部用node.js代替java了,只能说楼主预测完全错误。、
不错的文章
不错的文章
node必将崛起
误人子弟的文章
图的顺序是不是错了?
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
<及所有内容,版权所有 &#169;
C4Media Inc.
服务器由 提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司
京ICP备号-7
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。您的位置: >>
  科普文一则,说说我对Node.js的一些认识,以及我作为前端工程师为什么会向后端工程师推荐Node.js。
&Node.js 是服务器端的 JavaScript 运行环境,它具有无阻塞(non-blocking)和事件驱动(event-driven)等的特色,Node.js 采用V8引擎,同样,Node.js实现了类似 Apache 和 nginx 的web服务,让你可以通过它来搭建基于 JavaScript的Web App。&
  我想不仅仅是Node.js,当我们要引入任何一种新技术前都必须要搞清楚几个问题:
我们遇到了什么问题?
这项新技术解决什么问题,是否契合我们遇到的问题?
我们遇到问题的多种解决方案中,当前这项新技术的优势体现在哪儿?
使用新技术,带来哪些新问题,严重么,我们能否解决掉?
  我们的问题:Server端阻塞
  Node.js被设计用来解决服务端阻塞问题。下面通过一段简单的代码解释何为阻塞:
  Js代码:
//根据ID,在数据库中Persons表中查出Name var name = db.query("selcect name from persons where id=1"); //进程等待数据查询完毕,然后使用查询结果。 output("name")
  这段代码的问题是在上面两个语句之间,在整个数据查询的过程中,当前程序进程往往只是在等待结果的返回,这就造成了进程的阻塞。对于高并发,I/O密集行的网络应用中,一方面进程很长时间处于等待状态,另一方面为了应付新的请求不断的增加新的进程。这样的浪费会导致系统支持QPS远远小于后端数据服务能够支撑的QPS,成为了系统的瓶颈。而且这样的系统也特别容易被慢链接攻击(客户端故意不接收或减缓接收数据,加长进程等待时间)。
  如何解决阻塞问题
  可以引入事件处理机制解决这个问题。在查询请求发起之前注册数据加载事件的响应函数,请求发出之后立即将进程交出,而当数据返回后再触发这个事件并在预定好的事件响应函数中继续处理数据:
  Js代码:
//定义如何后续数据处理函数 function onDataLoad(name){
output("name"); } //发起数据请求,同时指定数据返回后的回调函数 db.query("selcect name from persons where id=1",onDataLoad);
  我们看到若按照这个思路解决阻塞问题,首先我们要提供一套高效的异步事件调度机制。而主要用于处理浏览器端的各种交互事件的JavaScript,相对于其他语言,至少有两个关键点特别适合完成这个任务。
  为什么JS适合解决阻塞问题
  首先JavaScript是一种函数式编程语言,函数编程语言最重要的数学基础是&演算(lambda calculus) -- 即函数对象可以作为其他函数对象的输入(参数)和输出(返回值)。
  这个特性使得为事件指定回调函数变得很容易。特别是JavaScript还支持匿名函数。通过匿名函数的辅助,之前的代码可以进行简写如下:
  Js代码:
db.query("selcect name from persons where id=1",function(name){
output(name); });
  还有另一个关键问题是,异步回调的运行上下文保持(本文暂称其为"状态保持")。我们先来看一段代码来说明何为状态保持:
  Js代码:
//传统同步写法:将查询和结果打印抽象为一个方法 function main(){
var id ="1";
var name = db.query("selcect name from persons where id="+ id);
output("person id:"+ id +", name:"+ name);
  前面的写法在传统的阻塞是编程中非常常见,但接下来进行异步改写时会遇到一些困扰:
  Js代码:
//异步写法: function main(){
var id ="1";
db.query("selcect name from persons where id="+ id,function(name){
output("person id:"+ id +", name:"+ name);//n秒后数据返回后执行回调
}); } main();
  细心的朋友可能已经注意到,当等待了n秒数据查询结果返回后执行回调时。回调函数中却仍然使用了main函数的局部变量"id",而"id"似乎应该在n秒前走出其作用域。为什么此时"id"仍然可以访问呢,这是因为JavaScript的另外一个重要语言特性:闭包(Closures)。接下来我来详解闭包的原委。
  在复杂的应用中,我们一定会遇到这类场景。即在函数运行时需要访问函数定义时的上下文数据(注意:一定要区分函数定义时和函数运行时两个不同的时刻)。特别是在异步编程模型中,函数的定义和运行又分处不同的时间段,那么保持上下文的问题变得更加突出了。因为我们在任务执行一半时把资源交出去没有问题,但当任务需要再次继续时我们必须还原现场。
  在这个例子中,db.query作为一个公共的数据库查询方法,把"id"这个业务数据传入给db.query,交由其保存是不太合适的。但我们可以稍作抽象,让db.query再支持一个需要保持状态的数据对象传入,当数据查询完毕后可以把这些状态数据原封不动的回传。如下:
  Js代码:
function main(){
var id ="1";
var currentState = new Object();
currentState.person_id =
db.query("selcect name from persons where id="+ id, function(name,state){
output("person id:"+ state.person_id +", name:"+ name);
},currentState);//注意currentState是db.query的第三个参数 } main();
  记住这种重要的思路,我们再看看是否还能进一步的抽象?可以的,不过接下的动作之前,我们还要了解在JavaScript中一个函数也是一个对象。一个函数实例fn除了函数体的定义之外,我们仍然可以在这个函数对象实例之本身扩展其他属性,如fn.a=1;受到这个启发我们尝试把需要保持的状态直接绑定到函数实例上:
  Js代码
function main(){
var id ="1";
var currentState =new Object();
currentState.person_id =
function onDataLoad(name){
output("person id:"+ onDataLoad.state.person_id +", name:"+ name);
onDataLoad.state = currentS//为函数指定state属性,用于保持状态
db.query("selcect name from persons where id="+ id, onDataLoad); }
  我们做了什么?生成了currentState对象,然后在函数onDataLoad定义时,将currentState绑定给onDataLoad这个函数实例。那么在onDataLoad运行时,就可以拿到定义时的state对象了。JavaScript的闭包特性就是内置了这个过程而已。
  在每个JavaScript函数运行时,都有一个运行时内部对象称为Execution Context,它包含如下Variable Object(VO, 变量对象), Scope Chain(作用域链)和"this" Value三部分。如图:
  其中变量对象VO,包含了所有局部变量的引用。对于main函数,局部变量"id"存储在VO.id内。看起来用VO来代替我们的currentSate最合适了。但main函数还可能嵌套在其他函数之内,所以我们需要ScopeChain,它是一个包含当前运行函数VO和其所有父函数scope的数组。
  所以在这个例子中,在onDataLoad函数定义时,就为默认为其绑定了一个[[scope]]属性指向其父函数的ExecutionContext的ScopeChain。而当函数onDataLoad执行时,就可以通过[[scope]]属性来访问父函数的VO对象来找到id,如果父函数的VO中没有id这个属性,就再继续向上查找其祖先的VO对象,直到找到id这个属性或到达最外层返回undefined。也正是因为这个引用,造成VO的引用计数不为0,在走出作用域时,才不会被垃圾回收。
  很多朋友觉得闭包较难理解,其实我们只要能明确的区分函数定义和函数运行两个时机,那么闭包就是让函数在运行时能够访问到函数定义时的所处作用域内的所有变量,或者说函数定义时能访问到什么变量,那么在函数运行时通过相同的变量名一样能访问到。
  关于状态保持是本文的重点,在我看到的多数Node.js的介绍文章中并没有详解这里,我们只是知道了要解决阻塞问题,但是JavaScript解决阻塞问题的优势到底在哪里,作为一名前端工程师,我想有必要花一些篇幅详细解释一下。
  而之所以我叫它&状态保持&因为还有一个非常相似的场景可以类比:
  用户从A页面提交表单到B页面,如果提交数据校验不通过,则需要返回A页面,同时保持用户在A页面填写的内容并提示用户修改不对的地方。从提交到校验出错再返回继续填写是一个包含网络交互的异步过程,这相当于填写表单这个任务过会儿再继续。
  在传统网页开发中,用户的状态通过请求传递到服务端,交由后端状态保持(类似交给db.query的currentSate)。而使用Ajax的网页,因为并未离开原页面,那么服务端只要负责校验用户提交的数据是否正确即可,发送错误,返回错误处相关信息即可,这就是所谓前端状态保持。可以看到这个场景里边服务端做的事情变少了,变纯粹了。正如我们的例子中db.query不再存储转发第三个state参数,变得更在轻量。
  我们看到通过JavaScript函数式语言特性,匿名函数支持和闭包很漂亮的解决了同步编程到异步编程转化过程中遇到的一系列最重要的问题。但JavaScript是否就是最好的?这就要回答我们引用新技术时需要考虑的最后一个问题了。
  使用Node.js是否带来额外的困扰,如何解决?
  Node.js性能真的是最好么?不用比较我们也可以得到结论,Node.js做无阻塞编程性能较难做到极致。何为极致?处理一个请求需要占用多少内存,多少cpu资源,多少带宽,有丁点浪费就不是极致。阻塞式编程浪费了大量进程资源只是在等待,导致大量内存和cpu的浪费。在这方面Node.js好很多,但也正是因为一些闭包等JavaScript内建机制也会导致资源的浪费,看下面的代码:
  Js代码:
function main(){
var id = "1";
var str = "..."; //这里局部变量str存储一个2M的字符串
db.query("selcect name from persons where id="+ id,function(name){
output("person id:"+ id +", name:"+ name);//n秒后数据返回后执行回调
}); } main();
  至少整个数据查询过程中,变量str所使用的2M内存并不会被释放,而str保持下去可能并没有意义。前面已经解释过闭包的原理,闭包并没有智能到只包起来今后可能被访问到的对象。即使不了解闭包的原理,也可以通过一段简单脚本验证这点:
  Js代码:
function main(){
var id ="1";
var str ="..."; //这里存储一个2M的字符串
window.setTimeout(function(){
//我们在这里设置断点
},10000) } main();
  我们在回调函数当中只设置一个断点,并不指明我们要访问哪个变量。然后我们在控制台监视一下,id和str都是可以拿到的。
  所以我来猜想一下,性能极端苛刻的场景,无阻塞是未来,但无阻塞发展下去,或者有更轻量的脚本引擎产生,或者JavaScript引擎可能要调整可以disable闭包,或者我们要通过给JS开发静态编译器在代码发布前自动优化我们的代码。
  静态编译是如今JavaScript技术领域的又一个热点,我们都知道JavaScript是解释型脚本语言,在运行时自动编译。但是运行时编译只是将代码转为机器码执行,却并未覆盖传统编译型语言在编译阶段所做的任务。比如,语法检查,接口校验,全局性能优化等等。
  最常见的JavaScript静态编译就是脚本压缩工具,在代码发布到线上之前,我们通过各种压缩工具,将代码压缩,达到减少网络传输量的问题。而在这个时间点,已经有越来越多的事情可做,比如:Google利用ClouserComplier提供的系列编译指令,让JavaScript更好的实现OO编程。也有GWT,CoffeeScript这样的项目,将其他语言编译为JavaScript。在淘宝我们在代码静态编译阶段来解决因JavaScript细粒度模块化改造引入各种性能问题,也用来对第三方提供JavaScript代码进行一定的安全检查。
  我们期待前面的代码经过静态编译器编译后变成如下结果:
  Js代码:
function main(){
var id = "1";
var str = "..."; //这里局部变量str存储一个2M的字符串
db.query("selcect name from persons where id="+ id,function(name){
output("person id:"+ id +", name:"+ name);
str = ""; //通过这一行,及时释放不必要的内存占用。 } main();
  除了性能方面的担忧,使用Node.js进行编程增加了代码编写的复杂度。因为我们习惯于阻塞式编程的写法,切换到异步模式编程,往往对于太多多层次的callback函数嵌套弄得不知所措。老赵最近开发了项目正是要解决这个问题,它让大家在遵守一些小的约定后,能够仍然保持同步编程的写法进行代码开发。写完的代码同样通过静态编译器编译成异步回调式模式的代码再交给JavaScript引擎执行。
  Node.js还要解决什么问题
  说了这么多,无阻塞编程要做的还远不止这些。首先需要一个高效的JS引擎,高效的事件池和线程池。另外几乎所有和Node.js交互的传统模块如文件系统,数据访问,HTTP解析,DNS解析都是阻塞式的,都需要额外改造。
  Node.js作者极其团队,正是认清问题所在以及JS解决这些问题方面的优势。基于Google开源的高效JavaScript引擎V8,贡献了大量的智慧和精力解决上述大部分问题后才有Node.js横空出世。
  当前Node社区如此火热,千余开源的,活跃在WebFramework,WebSocket,RPC,模板引擎,数据抓取服务,图形图像几乎所有工程领域。
  本文主要的信息来自Node.js作者在,上的分享。 而作为前端开发,着重讲了函数式编程,闭包对于无阻塞开发的重要意义。我期待这篇文章能够给前端和后端工程师都带来收获。
  同样作为前端开发,不得不再插几句,说说服务端JS能够解决的另一个问题:当前的Web开发前后端使用不同的语言,很多相同的业务逻辑要前后端分别用不同语言重复实现。比如越来越多重度依赖JavaScript的胖客户端应用,当客户浏览器禁用JavaScript时,则需要使用服务端语言将主业务流程再实现一次,这即是前端常说的&渐进增强&。
  当我们拥有了服务端JavaScript语言,我们自然就会想到能否利用Node.js做到&一次开发,渐进增强&。解决掉这个为小量用户,浪费大量时间的恼人的问题。这方面的实践,YAHOO仍然是先驱,早在一年多前开始YAHOO通过项目做了很多卓越的贡献,而淘宝自主开发的前端框架也有服务端运行的相关尝试。
  JavaScript在诞生之时就不仅仅是浏览器端工具,如今JavaScript能够再一次回到服务端展示拳脚,感谢V8,感谢NodeJS作者,团队和社区的诸多贡献者,祝Node好运,JavaScript好运。
  关于作者
  李穆,前端工程师,就职于淘宝广告技术部架构组,淘宝花名:李牧,专注淘宝广告引擎和业务系统前端开发。
Web前端热门文章
Web前端最新文章

我要回帖

更多关于 nodejs 写后端 的文章

 

随机推荐