设为首页 收藏本站
查看: 1126|回复: 0

[经验分享] 学习NodeJS第二天:漫谈NodeJS [收藏]

[复制链接]

尚未签到

发表于 2017-3-1 10:43:20 | 显示全部楼层 |阅读模式
From:http://blog.csdn.net/zhangxin09/archive/2010/09/10/5874632.aspx
新型的服务端正在进入我们的视野,让我们投入了关注的目光,例如近来的NodeJS算比较抢眼的一员。
之所以创造NodeJS,引用原作者Ryan语,目标就是为了可以更轻松地编写具有可伸缩性的网络程序。咋一看,这样的目标作为网络开发人员们何曾不想拥有。——于是看看Nodejs是怎么实现的。首先由浅入深说下简单的概念:无论是复杂的业务逻辑,还是简单的“HelloWorld”也罢,客户端发送链接过来,Web服务器肯定要一一全单照收,不会拒“链接”于千里之外。当中所说的性能指标就是我们日常会提到的——“并发(Concurrency)”。Web服务器是并发处理这些链接请求的。并发越高,服务器性能越好——到最终,大概是要解决著名C10K问题。在处理并发的这个技术问题上,NodeJS表现出来的,就是高并发、低消耗的佼佼者。 DSC0000.gif
NodeJS有一定性能优势,却引发了我们技术人员的浓厚兴趣。不免要问,NodeJS是如何办到的?NodeJS是开源项目,如果不打算直接通过源码了解,我们还是可以通过网上一点资讯了解。笔者收集了关于NodeJS的几遍文章、博客,略有心得,将它们想表达NodeJS的特点、缺点、相关原理、前期分析、选型等等各方的问题“共冶一炉”,说出个NodeJS初步分析的大概。
如果各位看官不太了解服务端的运作的话,我们稍微回顾一下请求Request这一环节的过程。现今多数的Web服务器中,有一条新的链接就会申请一条线程来负责处理至到这个Request周期结束,接着执行其他流程。可以想象,成千上万个链接便有成千上万条线程(Thread-spawning)。每条线程姑且以堆栈2MB的消耗去计算,一条条线程它们的累加都是不小的数目。如何优化和改进本身就是一个大问题,此外,使用系统线程,必须考虑线程锁的问题,否则造成堵塞主进程又是一个令人操心的难题。NodeJS则通过基于事件的异步模型绕开了基于线程模型的所带来的问题。NodeJS使用JavaScript单线程(Single-threaded)轮询事件,设计上比较简单,高并发时,不仅根本性的减少了线程创建和切换的开销(因而没有吓人的消耗),而且由于没有锁,也不会造成进程阻塞。每当有链接发起到服务端之后,NodeJS会透过epoll、kqueue、/dev/poll或select指令通知操作系统,有新链接到达,应执行指定的回调函数(Callback)。每个链接从成本上说只消耗一个堆(heapallocation)。
单线程的Nodejs?
NodeJS使用单线程就足以提供高速的并发能力?是的,实际上著名nginx也是基于单线程的。然而拜C++所赐,Node.js却拥有多线程的运行环境。NodeJS虽带有JS的名称,以JS为卖点,——确实也如此,轻盈的JS替C/C++跟开发人员打交道,但必须强调,JS终究是编写中间件的脚本语言,底层发挥作用的仍然是C/C++。为了实现这些设计目标,Node.js使用了GoogleV8并打包了其中的一些库:

  • libev实现了时间循环并封装了底层使用的具体的技术(如select,epoll等)。
  • 作者自己写的http-parser等协议和其他等等。
  其中libev正是实现多线程NodeJs的基础(edit on 2010-9-12:Are you sure to say so???有什么证据??)。JavaScript仍旧发挥脚本语言的本色,一方面将C/C++的复杂性屏蔽,一方面向程序员呈现优雅的API。Node.js在适合一些较轻松的场合,包括一些分离器Dispatcher、Request、BeansTalk、AMQP消息应该没有问题。但依据国外一些博客文章分析就是,实际生产中可能会意外频频,发生一个错误就会挂起node.js,所以单线程不太可靠或许是nodejs一个先天的缺点。另外,编写NodeJS的扩展仍需要出来高深的C++,恐怕须完善好C与JS之间的接口层,编写NodeJS扩展才是我辈能力范围内。写本文的时候,Node属新生事物,无须讳言,笔者没有太多的直观经验。究竟实际上有多少的情景允许我们一边计算,一边做其他的事情而稳定无虞的呢?希望可以有待更多的观察。
上述的几点,的确提到了“基于线程模型”v.s“基于事件模型”之争,目的就在于,除了明晰分辨它们的利弊之外,还不能不回答这样一个问题:既然“基于线程模型”消耗得那么厉害,那么为什么现在这么多的Apaches、IIS都运行得好好的?
基于事件的Web服务器相对是比较新的概念,可以做到比较好的性能,因而受到推崇一点不意外,像Node.js那样的,——而传统的基于线程的模型服务器成熟程度高,况且仍不断地发展,例如Apache的PHP会派生出很多的OS线程来解决并发的问题,若一个请求挂起了其所在的线程,可以保证其他的线程也不会受到影响,不会冻结整个服务器进程,显得也比较合理。必须指出的是,像对于如何处理并发来选择“基于线程模型”v.s“基于事件模型”这样的讨论,业界一直存在,并不是说基于事件模型的一定优秀无敌,尚有许多一一斟酌讨论的地方,具体如何就不一一展开了。
那么,Node.js的优势到底在哪?

应对长链接的压力
例如,某网站pv非常可观,与用户互动频繁,那么它的线路总是处于高峰,自然它的网络进进出出肯定非常频繁,势必要求后台要赶快处理好前一个请求,以便接着有时间来处理一个请求,越快越好、越高效。好在,我们的请求大小都不是很大,通常几十字节(如http://domain:80,一个GET操作,cookies不大的话),控制线程在一个很小的单位,如此往返一个来回很快搞掂。那当然属于I/O最简单的情况了,稍为复杂的一些就是POST表单、文件上传等的任务。但好在不是每个链接皆如此,服务器还可以吃得消久一点的链接。可是,这时候,来问题了——
话说Web2.0时兴的元素,WebIM、WebGAME、Web协作……无一不需求长链接为其服务的。长链接,或长轮询,是企图突破现有HTTPv1.1链接模型,把无态(Stateless)的点对点链接变为人们理想的有态(Stateful),也就是Request/Response互不分离,总是在线有沟通着。实际情形HTTP并没有提供这种的API或者说服务。当前我们大抵采用折衷的方法:打开一HTML页面立刻发送服务端的AJAX请求,就算是没有内容的请求都好,没有关系,服务器就千万别像普通AJAX那样接收请求,处理流程后就返回Repsonse,不要立刻返回内容而是等待,换言之,就是保持链接。只是在有消息发出的时候才返回Response然后浏览器渲染Response内容。例如,有好友发悄悄话给你,通过服务器发送到你浏览器上显示,然后立刻发起新的请求,让彼此之间的链接一直保持下去。
介绍前面的这么多,无非想说明,客户端与服务端一旦链接后,除非用户关闭浏览器,否则是不会断开keep-alive链接的。这样,对于同时维系着数十条或者数百条(聊天室)的connection的服务器,一直非空闲,还要顾上各方面资源(CPUusage、consumingmemory……),显然不是一件容易事情,甚至如项目“开心网”那样成千上万笔connection场景就是对服务端极大的考验,如果占用的线程不能得到迅速释放,将会给服务器带来灾难性的后果!
于是一些WebSerever认真考虑到这点,在新版中提供适应长链接的场景,例如Jetty很早的时候就提供一个J2EE容器的解决方案,与Comet的通讯协议对接上。每个Server的架构不一,然而如何改进和改进目标都有参考意义,但改进已是必然了,就要重新考虑WebI/O,提供足够快而稳定性能适应长链接的场景。明显,不得不重新考虑服务端的设计了,然而,背后要考虑的事情就多了。总之,可以想象任务艰巨性,不仅要考虑前方I/O高并发,低响应时间的请求,还要考虑整套的服务供应者怎么去资源调控,具体如负载平衡(LoadBalancing)、动态DNS切换、DB的集群、多个文件镜像的问题,往往配合起来就有许多不可预料的问题发生。一个环节有问题真个系统的堵塞了。这一启承转合要处理好。
DSC0001.jpg

不是有WebSocket标准吗?HTML5的世界尽管在移动平台上很热闹,普通浏览器升级却觉得是另一回事。如果现在一下子都是支持WebSocket的浏览器,那不用说准是皆大欢喜了,但事实和将来的预测表明WebSocket完全是另外一回事,咱和咱用户面对的仍旧那些僵硬不化的IE6……所以说在WebSocket不现实的今天,将善于“长链接”的Nodejs派上用场便有很充分的理由。
p.s:……包括用flashsocket组件那些hack的都不算。
发挥事件模型的威力
Node.Js带来了一股清新之风,与其使用JS乃是密不可分的。这次,神奇的JavaScript又一次成为了胶水语言,为“基于事件驱动模型(Evnent-based)”开发埋下重要的伏笔。事件本质上一个时空不一致的非线性模型,或所谓的“异步(Asynchronization)”。事件发生的顺序按照外界对其发出的时刻而确定,有的在先,有的在后,有时也可以齐头并进,一起同时触发,——结束时也可以快的快、慢的慢。(呵呵,本人有些无聊,突然想起小学课本,华罗庚那篇的《统筹方法》“……想泡壶茶喝。当时的情况是:开水没有。开水壶要洗,茶壶茶杯要洗;火已升了,茶叶也有了。怎么办?……”,实有异曲同工之妙!)。具体说,就是在一方面处理诸如数据库查询/存储、磁盘读写、网络延时那一类费时的任务,一方面处理内存中高速的运作,来作一个合理地平衡调度。当然,回归这一点的要求与多线性模型的I/O要求是无异的。总之不是直接的某个函数method()去执行(那是同步的方式,Node.js也支持),而是写回调callback;如果换了是同步方式,就必须等待上一个任务结束,才能开始下一个任务。本来可以齐头并进的机会却白白浪费掉了。换言之,大多数操作往往是I/O的等待,不过NodeJS底层对于JavaScript该层面来说,由后台线程调用JavaScript函数,因此无碍JS代码的执行,实现异步的操作,即“非阻塞”。例如下面摘自文档的一个例子:

  • var   
  •      posix = require("posix")  
  •     ,sys = require("sys");      
  •       
  • var promise = posix.unlink("/tmp/hello");promise.addCallback(function () {        
  •     sys.puts("successfully deleted /tmp/hello");   
  • });  
var posix = require("posix"),sys = require("sys");     var promise = posix.unlink("/tmp/hello");promise.addCallback(function () {      sys.puts("successfully deleted /tmp/hello");  });如果删除文件成功,触发success事件执行addCallback()所定义的回调函数;即是删除文件失败,产生wait的信号,直至timeout的时限,也不会阻塞其他JS代码的执行。在Node.js的API中,到处使用事件的概念,包括许多方法都设有“同步”和“异步”的两种方式供选择,故所以我们不用担心写的代码会阻塞Node.js的I/O。
个人认为,从感觉而言,两者之间还有一点的差异可能是,多线性模型不像编写事件那么自然。定义事件起来隐约会有一种写“DSL”的感觉,尤其在JS这个FunctionFirst的脚本帮助下。另外可以参考一下前一篇《node.js引言 》的博文,此处不再复述。
题外话:貌似AJAXAIR in Js呈现了也是一种异步调用方式(记得SQLquery时语法相似)。
DSC0002.jpg

  事件循环的console模拟图
实际上,NodeJs不是第一家标榜事件的WebServer,早在NodeJs之前,在各种语言中都有事件的实现,不能不提的就是nginx。不过使用JavaScript还属于头一遭吧。过去几年可以说是JS引擎发展的高峰期,就连最保守的微软也要IE9把落后的JS解释速度争回来,亲爱的服务端方面却又怎么按耐的住呢?自然,革新速度后,JSVM引入到Serverside的工作便是一个顺理成章的事。
话说回来基于事件理念的Server。Node.js最初得益于Ruby的EventMachine和Python的Twisted,将包括各种I/O操作定义在回调函数中,通过事件不断轮询任务列表来触发那些Callback,——并且NodeJS有创新的地方,就是提出新的思路来呈现事件机制。从原理上讲,Node.js不仅仅是一个库,而是尝试利用语言机制来构建的事件模型。EventMachine或Twisted不是这样,它们都是在代码开始和结束的时候插入回调函数来完成一个阻塞的调用,然后这个过程的启用,就用:


  • EventMachine::run()   
EventMachine::run() 而Node.js没有这种代码顺序的限制,可以在定义代码之后再插入新的代码,继续参与事件。同时node.js也不会TwistedPython那样提供“延时线程(deferto thread)”,实际是堵塞代码的“陷阱”。
尽管我们这里说的事件模型好像比较简单,但是许多的基础设施对异步操作的支持的不足的,尤其普通用户根本不会自己去创建业务事件。相关内容在介绍NodeJS的Slide有介绍(搜索jsconf.pdf),说明为什么Nodejs出现之前没有类似NodeJS的“物体”出现,同时也说明设计nodejs要克服的难关。

结语
最后一点,谈谈node.js为什么选择GoogleV8的JS引擎而不是另一个著名的SpiderMonkey引擎。抛开速度等的硬性指标不表,可能是SpiderMonkey源码仍然比较复杂的缘故,不好把玩,人们自然就青睐于虽然是C++的V8了。
本文介绍了一位JS爱好者对NodeJS以及后台初步感性的了解,没有深刻的认识,竟也成文,看官们可作取舍(trade-off),将就来读,并请积极献言,一同讨论。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-348723-1-1.html 上篇帖子: JAVA应用开发日志解决方案 下篇帖子: Spring整合DWR comet 实现无刷新 多人聊天室
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表