了解node.js看这一篇就可以了
Posted 技能日记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了了解node.js看这一篇就可以了相关的知识,希望对你有一定的参考价值。
简单的说 Node.js 就是运行在服务端的 javascript。作为一个由JAVA入行的程序员,2010年加入阿里巴巴团队开始接触Node.js,感受到它很多有魅力的特性。那时我们的互联网领域尤其是编程方面与欧美存在至少4年以上的技术滞后;2009年花名“朴灵”的阿里技术大咖将前一年才诞生但在欧美立刻风靡的node.js引入阿里内部,从此阿里成为在我国开展node.js应用和普及的先锋和主力。记得那时候在阿里的内部分享中node.js从来都是作为重要的主题之一,而我这样资质愚钝的老派技术员也立刻被node的魅力所吸引。及至现在node.js已经成阿里技术体系中及其重要的一环,更可喜的是2017年阿里egg.js框架团队工程师张秋怡被吸纳为CTC成员,有资格为node.js技术的未来发展做出决策。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。是一个异步非阻塞的事件驱动I/O服务端JavaScript环境,基于Google的V8引擎。V8引擎执行Javascript的速度非常快,性能非常好。
如果你去年注意过编程方面的新闻,我敢说你至少看到node.js不下四五次。那么问题来了“node.js是什么?”。有些人没准会告诉你“这是一种通过JavaScript语言开发web服务端的东西”。如果这种晦涩解释还没把你搞晕,你没准会接着问:“为什么我们要用node.js?”,别人一般会告诉你:node.js有非阻塞,事件驱动I/O等特性,从而使高并发(high concurrency)的轮询(Polling)在构建的应用中成为可能。
当你看完这些解释觉得跟看天书一样的时候,你估计也懒得继续问了。不过没事。这篇文章就是在避开高端术语的同时,帮助你你理解node.js的。
浏览器给网站发请求的过程从起源到现在一直没怎么变过。当浏览器给网站发了请求。服务器收到了请求,然后开始搜寻被请求的资源。如果有需要,服务器还会查询一下数据库,最后把响应结果传回浏览器。不过,在传统的web服务器中(比如Apache),每一个请求都会让服务器创建一个新的进程来处理这个请求。
后来出现了Ajax。有了Ajax,我们就不用每次都请求一个完整的新页面了,取而代之的是,每次只请求需要的部分页面信息就可以了。这显然是一个进步。但是比如你要建一个类似人人网那样的刷朋友新鲜事的网站(这和我们设备推送数据的原理是一样的,甚至请求更加复杂和频繁),你的好友会随时的推送新的状态,然后你的新鲜事会实时自动刷新。要达成这个需求,我们需要让用户一直与服务器保持一个有效连接。目前最简单的实现方法,就是让用户和服务器之间保持长轮询(long polling)。
HTTP请求不是持续的连接,你请求一次,服务器响应一次,然后就完了。长轮询是一种利用HTTP模拟持续连接的技巧。具体来说,只要页面载入了,不管你需不需要服务器给你响应信息,你都会给服务器发一个Ajax请求。这个请求不同于一般的Ajax请求,服务器不会直接给你返回信息,而是它要等着,直到服务器觉得该给你发信息了,它才会响应。比如,你的好友发了一条新鲜事,服务器就会把这个新鲜事当做响应发给你的浏览器,然后你的浏览器就刷新页面了。浏览器收到响应刷新完之后,再发送一条新的请求给服务器,这个请求依然不会立即被响应。于是就开始重复以上步骤。利用这个方法,可以让浏览器始终保持等待响应的状态。虽然以上过程依然只有非持续的HTTP参与,但是我们模拟出了一个看似持续的连接状态。
我们再看传统的服务器(比如Apache)。每次一个新用户连到你的网站上,你的服务器就得开一个连接。每个连接都需要占一个进程,这些进程大部分时间都是闲着的(比如等着你好友发新鲜事,等好友发完才给用户响应信息。或者等着数据库返回查询结果什么的)。虽然这些进程闲着,但是照样占用内存。这意味着,如果用户连接数的增长到一定规模,你服务器没准就要耗光内存直接瘫了。
这种情况怎么解决?解决方法就是刚才上边说的:非阻塞和事件驱动。这些概念在我们谈的这个情景里面其实没那么难理解。你把非阻塞的服务器想象成一个loop循环,这个loop会一直跑下去。一个新请求来了,这个loop就接了这个请求,把这个请求传给其他的进程(比如传给一个搞数据库查询的进程),然后响应一个回调(callback)。完事了这loop就接着跑,接其他的请求。这样下来。服务器就不会像之前那样傻等着数据库返回结果了。
如果数据库把结果返回来了,loop就把结果传回用户的浏览器,接着继续跑。在这种方式下,你的服务器的进程就不会闲着等着。从而在理论上说,同一时刻的数据库查询数量,以及用户的请求数量就没有限制了。服务器只在用户那边有事件发生的时候才响应,这就是事件驱动。
FaceBook的FriendFeed是用基于Python的非阻塞框架Tornado (知乎也用了这个框架) 来实现上面说的新鲜事功能的。不过,Node.js就比前者更妙了。Node.js的应用是通过javascript开发的,然后直接在Google的变态V8引擎上跑。用了Node.js,你就不用担心用户端的请求会在服务器里跑了一段能够造成阻塞的代码了。因为javascript本身就是事件驱动的脚本语言。你回想一下,在给前端写javascript的时候,更多时候你都是在搞事件处理和回调函数。javascript本身就是给事件处理量身定制的语言。
Node.js从2008年诞生到现在还处于发展阶段。如果你想开发一个基于Node.js的应用,你可能会需要写一些很比较底层的代码。但是目前的浏览器已经有限的采用了WebSocket技术了,从而长轮询也会消失。在Web开发里,Node.js这种类型的技术只会变得越来越重要。
下面我们再站在比较专业的角度来分析node.js技术
一、Node.js的整体感知
Node.js是JavaScript的一个服务端运行环境。使得JS可以像php、Python等语言一样可以进行服务端程序开发。传统JS中的DOM和BOM被剔除,首先它遵循EMCAScript标准实现核心JavaScript,在此基础上,又实现了诸如模块、包、文件系统、网络通信和操作系统API等新的功能。
Node内部采用Google Chrome的V8引擎,作为JavaScript语言的解释器,在该引擎当中执行ES代码,另外Node平台还提供了一些组件,通过自行开发的Libuv库,用于调度操作系统资源。
二、Node.js在Web当中的作用
1、 做动态网站(目前我们使用的结构)
2、分发数据请求,渲染html页面(阿里内部经典架构)
三、Node.js的核心特性(事件驱动和非阻塞)
首先先了解一下进程和线程的概念。
进程:操作系统为应用程序分配资源的一个单位。用来给应用程序提供一个运行环境。
线程:用来执行应用程序中的代码,一个进程内部,可以有很多线程。但在一个线程内部,同时只能做一件事。
某个应用程序启动之后会默认创建一个主线程,用于执行我们的代码。但用户的代码当中通常会有一下耗费时间的阻塞代码,对于Java、.Net、PHP这种一个进程当中可以有多个线程的应用程序,在执行代码的过程当中,会开辟多个线程去分别执行那些主线程当中会发生阻塞的代码。
多线程存在的问题:
创建线程耗费资源。
线程数量有限。
线程之间共享数据,同步状态都比较麻烦。
CPU在不同线程之间转换非常耗时
但对于Node来说,由于Node采用Chrome V8引擎处理JavaScript脚本,而V8最大的特点是单线程运行。即Node内部只允许有一个线程,故为了提高代码的执行效率,避免代码阻塞的情况的出现。Node当中采用大量的异步操作。即Node当中所有会发生代码阻塞的操作都是异步的。
3.1、Node当中的事件驱动模型
在Node的内部有一个事件队列,事件队列由一对一对的键值对构成,即事件:对应的回调函数的形式。Node的主线程在执行用户的代码的过程中,先执行那些非阻塞的代码,当执行到类似于文件操作或网络操作之类的阻塞代码,则会根据其任务代码出现的顺序,将其放入事件队列当中,与该任务相关的代码放在该任务的回调函数当中。
即Node主线程在执行程序的过程,会直接执行那些非阻塞的代码,但如果在执行的过程中,遇到阻塞型的代码,主线程的处理仅仅为将该事件函数及其对应的回调函数放进事件队列当中,暂不执行。等主线程把程序当中那些非阻塞的代码全部执行完了之后,再按照从上到下的顺序,从事件队列当中来取事件任务来执行。
——引用自node.js官方文档
当Node执行事件队列当中的任务时,先执行阻塞事件,然后再开始执行该事件对应的回调函数,在执行其回调的函数的过程当中,同样也是先执行那些非阻塞的代码,对于遇到回调函数当中的阻塞事件时,同样暂不执行,将该事件及其对应的回调函数,插入事件队列的尾部。
3.2、Node内部的线程池模型
Node内部有一个事件循环(Event Loop)来依次从事件队列(Event Queue)当中取事件来执行,对于事件队列的一个键值对来说,从开始执行该阻塞事件,到开始执行该事件对应的回调函数的过程当中,可能会设计文件或网络操作,可能会阻塞很长时间,但实际上代码不会卡死在这里。因为这里的阻塞操作不是由node主线程来做的,而是交给另一个线程来完成的。
在node底层维护了一个线程池,该线程池内部又维护了很多的线程,当Event Loop在依次执行事件队列当中的事件时,若遇到阻塞操作,就交给线程池当中的线程来做,这对于主线程没有任何影响,主线程仍然继续沿着Event Loop往下走,接着去处理事件队列当中的下一个事件。
不一定是等事件队列当中的上一个事件的回调函数执行完成之后,才开始执行下一个事件。
当线程池当中的线程执行完了交给它的任务之后,它会通知正处于Event Loop当中的主线程,让其来执行对应的回调函数。然后该线程得以释放,返回到线程池当中。
在Node内部实际上是多线程的,对于类似于文件操作之类的阻塞操作,让另一个线程去耗费这个时间,而主线程在一直不停的工作。一般我们说node是单线程的,是因为对于外界变成的API来说,所有的任务都是由一个线程来完成,只有内部的一些阻塞操作才交给内部的线程池来完成。
Node将所有的阻塞操作交给内部实现的线程池,而Node主线程本身则负责不停的往返调度。非阻塞最大的性能优势就是,能充分利用单核CPU的优势,如果让单核CPU不断地在多个线程之间切换,会造成很大的性能损耗。Node采用基于事件驱动的异步I/O模型,极大地提高了HTTP服务器的并发性能。
以上是关于了解node.js看这一篇就可以了的主要内容,如果未能解决你的问题,请参考以下文章
[建议速看] 学习Python的小白了解 Python看这一篇就够了!