什么是Node.js?Node.js详细介绍
简介
如果您听说过Node,或者阅读过一些文章,宣称Node是多么多么的棒,那么您可能会想:“Node究竟是什么东西?”尽管不是针对所有人的,但Node可能是某些人的正确选择。
为试图解释什么是Node.js,本文探究了它能解决的问题,它如何工作,如何运行一个简单应用程序,最后,Node何时是和何时不是一个好的解决方案。本文不涉及如何编写一个复杂的Node应用程序,也不是一份全面的Node教程。阅读本文应该有助于您决定是否应该学习Node,以便将其用于您的业务。
Node旨在解决什么问题?
Node公开宣称的目标是“旨在提供一种简单的构建可伸缩网络程序的方法”。当前的服务器程序有什么问题?我们来做个数学题。在Java™和PHP这类语言中,每个连接都会生成一个新线程,每个新线程可能需要2MB配套内存。在一个拥有8GBRAM的系统上,理论上最大的并发连接数量是4,000个用户。随着您的客户端基础的增长,您希望您的web应用程序支持更多用户,这样,您必须添加更多服务器。当然,这会增加业务成本,尤其是服务器成本、运输成本和人工成本。除这些成本上升外,还有一个技术问题:用户可能针对每个请求使用不同的服务器,因此,任何共享资源都必须在所有服务器之间共享。例如,在Java中,静态变量和缓存需要在每个服务器上的JVMs之间共享。这就是整个web应用程序架构中的瓶颈:一个服务器能够处理的并发连接的最大数量。
Node解决这个问题的方法是:更改连接连接到服务器的方式。每个连接都创建一个进程,该进程不需要配套内存块,而不是为每个连接生成一个新的OS线程(并向其分配一些配套内存)。Node声称它绝不会死锁,因为它根本不允许使用锁,它不会直接阻塞I/O调用。Node还宣称,运行它的服务器能支持数万个并发连接。事实上,Node通过将整个系统中的瓶颈从最大连接数量更改到单个系统的流量来改变服务器面貌。
现在您有了一个能处理数万条并发连接的程序,那么您能通过Node实际构建什么呢?如果您有一个web应用程序需要处理这么多连接,那将是一件很“恐怖”的事!那是一种“如果您有这个问题,那么它根本不是问题”的问题。在回答上面的问题之前,我们先看看Node如何工作以及它被设计的如何运行。
Node肯定不是什么
没错,Node是一个服务器程序。但是,它肯定不像Apache或Tomcat。那些服务器是独立服务器产品,可以立即安装并部署应用程序。通过这些产品,您可以在一分钟内启动并运行一个服务器。Node肯定不是这种产品。Apache能添加一个PHP模块来允许开发人员创建动态web页,使用Tomcat的程序员能部署JSPs来创建动态web页。Node肯定不是这种类型。
在Node的早期阶段(当前是version0.4.6),它还不是一个“运行就绪”的服务器程序,您还不能安装它,向其中放置文件,拥有一个功能齐全的web服务器。即使是要实现web服务器在安装完成后启动并运行这个基本功能,也还需要做大量工作。
Node如何工作
Node本身运行V8JavaScript。等等,服务器上的JavaScript?没错,您没有看错。服务器端JavaScript是一个相对较新的概念,这个概念是大约两年前在developerWorks上讨论AptanaJaxer产品时提到的(参见参考资料)。尽管Jaxer一直没有真正流行,但这个理念本身并不是遥不可及的—为何不能在服务器上使用客户机上使用的编程语言?
什么使V8?V8JavaScript引擎是Google用于他们的Chrome浏览器的底层JavaScript引擎。很少有人考虑JavaScript在客户机上实际做了些什么?实际上,JavaScript引擎负责解释并执行代码。使用V8,Google创建了一个以C++编写的超快解释器,该解释器拥有另一个独特特征;您可以下载该引擎并将其嵌入任何应用程序。它不仅限于在一个浏览器中运行。因此,Node实际上使用Google编写的V8JavaScript引擎并将其重建为在服务器上使用。太完美了!既然已经有一个不错的解决方案可用,为何还要创建一种新语言呢?
事件驱动编程
许多程序员接受的教育使他们认为,面向对象编程是完美的编程设计,而对其他编程方法不屑一顾。Node使用一个所谓的事件驱动编程模型。
清单1.客户端上使用jQuery的事件驱动编程
//jQuerycodeontheclient-sideshowinghowEvent-Drivenprogrammingworks //Whenabuttonispressed,anEventoccurs-dealwithit //directlyrighthereinananonymousfunction,whereallthe //necessaryvariablesarepresentandcanbereferenceddirectly $("#myButton").click(function(){ if($("#myTextField").val()!=$(this).val()) alert("Fieldmustmatchbuttontext"); });
实际上,服务器端和客户端没有任何区别。没错,这没有按钮点击操作,也没有向文本字段键入的操作,但在一个更高的层面上,事件正在发生。一个连接被建立—事件!数据通过连接接收—事件!数据通过连接停止—事件!
为什么这种设置类型对Node很理想?JavaScript是一种很棒的事件驱动编程语言,因为它允许匿名函数和闭包,更重要的是,任何写过代码的人都熟悉它的语法。事件发生时调用的回调函数可以在捕获事件处编写。这样,代码容易编写和维护,没有复杂的面向对象框架,没有接口,没有在上面架构任何内容的潜能。只需监听事件,编写一个回调函数,然后,事件驱动编程将照管好一切!
示例Node应用程序
最后,我们来看一些代码!让我们将讨论过的所有内容综合起来,创建我们的第一个Node应用程序。由于我们已经知道,Node对于处理高流量应用程序很理想,我们就来创建一个非常简单的web应用程序—一个为实现最大速度而构建的应用程序。下面是“老板”交代的关于我们的样例应用程序的具体要求:创建一个随机数字生成器RESTfulAPI。这个应用程序应该接受一个输入:一个名为“number”的参数。然后,应用程序返回一个介于0和该参数之间的随机数字,并将生成的数字返回调用者。由于“老板”希望它成为一个广泛流行的应用程序,因此它应该能处理50,000个并发用户。我们来看看代码:
清单2.Node随机数字生成器
//thesemodulesneedtobeimportedinordertousethem. //Nodehasseveralmodules. Theyarelikeany#include //orimportstatementinotherlanguages varhttp=require("http"); varurl=require("url"); //ThemostimportantlineinanyNodefile. Thisfunction //doestheactualprocessofcreatingtheserver. Technically, //Nodetellstheunderlyingoperatingsystemthatwhenevera //connectionismade,thisparticularcallbackfunctionshouldbe //executed. Sincewe'recreatingawebservicewithRESTAPI, //wewantanHTTPserver,whichrequiresthehttpvariable //wecreatedinthelinesabove. //Finally,youcanseethatthecallbackmethodreceivesa'request' //and'response'objectautomatically. Thisshouldbefamiliar //toanyPHPorJavaprogrammer. http.createServer(function(request,response){ //Theresponseneedstohandlealltheheaders,andthereturncodes //Thesetypesofthingsarehandledautomaticallyinserverprograms //likeApacheandTomcat,butNoderequireseverythingtobedoneyourself response.writeHead(200,{"Content-Type":"text/plain"}); //Hereissomeunique-lookingcode. ThisishowNoderetrives //parameterspassedinfromclientrequests. Theurlmodule //handlesallthesefunctions. Theparsefunction //deconstructstheURL,andplacesthequerykey-valuesinthe //queryobject. Wecanfindthevalueforthe"number"key //byreferencingitdirectly-thebeautyofJavaScript. varparams=url.parse(request.url,true).query; varinput=params.number; //ThesearethegenericJavaScriptmethodsthatwillcreate //ourrandomnumberthatgetspassedbacktothecaller varnumInput=newNumber(input); varnumOutput=newNumber(Math.random()*numInput).toFixed(0); //Writetherandomnumbertoresponse response.write(numOutput); //Noderequiresustoexplicitlyendthisconnection. Thisisbecause //Nodeallowsyoutokeepaconnectionopenandpassdatabackandforth, //thoughthatadvancedtopicisn'tdiscussedinthisarticle. response.end(); //Whenwecreatetheserver,wehavetoexplicitlyconnecttheHTTPserverto //aport. StandardHTTPportis80,sowe'llconnectittothatone. }).listen(80); //OutputaStringtotheconsoleoncetheserverstartsup,lettingusknoweverything //startsupcorrectly console.log("RandomNumberGeneratorRunning...");
将上面的代码放到一个名为“random.js”的文件中。现在,要启动这个应用程序并运行它(进而创建HTTP服务器并监听端口80上的连接),只需在您的命令提示中输入以下命令:%noderandom.js。下面是服务器已经启动并运行时它看起来的样子:
root@ubuntu:/home/moila/ws/mike#noderandom.js RandomNumberGeneratorRunning...
访问应用程序
应用程序已经启动并运行。Node正在监听任何连接,我们来测试一下。由于我们创建了一个简单的RESTfulAPI,我们可以使用我们的web浏览器来访问这个应用程序。键入以下地址(确保您完成了上面的步骤):http://localhost/?number=27。
您的浏览器窗口将更改到一个介于0到27之间的随机数字。单击浏览器上的“重新载入”按钮,将得到另一个随机数字。就是这样,这就是您的第一个Node应用程序!
Node对什么有好处?
到此为止,应该能够回答“Node是什么”这个问题了,但您可能还不清楚什么时候应该使用它。这是一个需要提出的重要问题,因为Node对有一些东西有好处,但相反,对另一些东西而言,目前Node可能不是一个好的解决方案。您需要小心决定何时使用Node,因为在错误的情况下使用它可能会导致一个多余编码的LOT。
它对什么有好处?
正如您此前所看到的,Node非常适合以下情况:您预计可能有很高的流量,而在响应客户端之前服务器端逻辑和处理所需不一定是巨大的。Node表现出众的典型示例包括:
1.RESTfulAPI
提供RESTfulAPI的web服务接收几个参数,解析它们,组合一个响应,并返回一个响应(通常是较少的文本)给用户。这是适合Node的理想情况,因为您可以构建它来处理数万条连接。它还不需要大量逻辑;它只是从一个数据库查找一些值并组合一个响应。由于响应是少量文本,入站请求时少量文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的API需求。
2.Twitter队列
想像一下像Twitter这样的公司,它必须接收tweets并将其写入一个数据库。实际上,每秒几乎有数千条tweets达到,数据库不可能及时处理高峰时段需要的写入数量。Node成为这个问题的解决方案的重要一环。如您所见,Node能处理数万条入站tweets。它能迅速轻松地将它们写入一个内存排队机制(例如memcached),另一个单独进程可以从那里将它们写入数据库。Node在这里的角色是迅速收集tweet并将这个信息传递给另一个负责写入的进程。想象一下另一种设计—一个常规PHP服务器自己试图处理对数据库的写入—每个tweet将在写入数据库时导致一个短暂的延迟,这是因为数据库调用正在阻塞通道。由于数据库延迟,一台这样设计的机器每秒可能只能处理2000条入站tweets。每秒100万条tweets需要500个服务器。相反,Node能处理每个连接而不会阻塞通道,从而能捕获尽可能多的tweets。一个能处理50,000条tweets的Node机器只需要20个服务器。
3.映像文件服务器
一个拥有大型分布式网站的公司(比如Facebook或Flickr)可能会决定将所有机器只用于服务映像。Node将是这个问题的一个不错的解决方案,因为该公司能使用它编写一个简单的文件检索器,然后处理数万条连接。Node将查找映像文件,返回文件或一个404错误,然后什么也不用做。这种设置将允许这类分布式网站减少它们服务映像、.js和.css文件等静态文件所需的服务器数量。
它对什么有坏处?
当然,在某些情况下,Node并非理想选择。下面是Node不擅长的领域:
1.动态创建的页
目前,Node没有提供一种默认方法来创建动态页。例如,使用JavaServerPages(JSP)技术时,可以创建一个在这样的JSP代码段中包含循环的index.jsp页。Node不支持这类动态的、HTML驱动的页面。同样,Node不太适合作为Apache和Tomcat这样的网页服务器。因此,如果您想在Node中提供这样一个服务器端解决方案,必须自己编写整个解决方案。PHP程序员不想在每次部署web应用程序时都编写一个针对Apache的PHP转换器,当目前为止,这正是Node要求您做的。
2.关系数据库重型应用程序
Node的目的是快速、异步和非阻塞。数据库并不一定分享这些目标。它们是同步和阻塞的,因为读写时对数据库的调用在结果生成之前将一直阻塞通道。因此,一个每个请求都需要大量数据库调用、大量读取、大量写入的web应用程序非常不适合Node,这是因为关系数据库本身就能抵销Node的众多优势。(新的NoSQL数据库更适合Node,不过那完全是另一个主题了。)
结束语
问题是“什么是Node.js?”应该已经得到解答。阅读本文之后,您应该能通过几个清晰简洁的句子回答这个问题。如果这样,那么您已经走到了许多编码员和程序员的前面。我和许多人都谈论过Node,但它们对Node究竟是什么一直很迷惑。可以理解,他们具有的是Apache的思维方式—服务器是一个应用程序,将HTML文件放入其中,一切就会正常运转。而Node是目的驱动的。它是一个软件程序,使用JavaScript来允许程序员轻松快速地创建快速、可伸缩的web服务器。Apache是运行就绪的,而Node是编码就绪的。
Node完成了它提供高度可伸缩服务器的目标。它并不分配一个“每个连接一个线程”模型,而是使用一个“每个连接一个流程”模型,只创建每个连接需要的内存。它使用Google的一个非常快速的JavaScript引擎:V8引擎。它使用一个事件驱动设计来保持代码最小且易于阅读。所有这些因素促成了Node的理想目标—编写一个高度可伸缩的解决方案变得比较容易。
与理解Node是什么同样重要的是,理解它不是什么。Node并不是Apache的一个替代品,后者旨在使PHPweb应用程序更容易伸缩。事实确实如此。在Node的这个初始阶段,大量程序员使用它的可能性不大,但在它能发挥作用的场景中,它的表现非常好。
将来应该期望从Node得到什么呢?这也许是本文引出的最重要的问题。既然您知道了它现在的作用,您应该会想知道它下一步将做什么。在接下来的一年中,我期待着Node提供与现有的第三方支持库更好地集成。现在,许多第三方程序员已经研发了用于Node的插件,包括添加文件服务器支持和MySQL支持。希望Node开始将它们集成到其核心功能中。最后,我还希望Node支持某种动态页面模块,这样,您就可以在HTML文件中执行在PHP和JSP(也许是一个NSP,一个Node服务器页)中所做的操作。最后,希望有一天会出现一个“部署就绪”的Node服务器,可以下载和安装,只需将您的HTML文件放到其中,就像使用Apache或Tomcat那样。Node现在还处于初始阶段,但它发展得很快,可能不久就会出现在您的视野中。