1
为什么要学习Node.js?
Node.js是目前非常火热的技术,可是作为一个前端开发人员,为什么要学Node.js?
说Node.js优劣的文章,网上一大堆,在这儿就不过多描述了。技术是服务于业务的,学技术最终目的就是为了更高的收入,咱们就是这么俗,那就从以下两点来看看,为什么要学Node.js
从工作上来说
首先第一点,很多大公司都在用Node.js,并且很多大厂的招聘“高级前端工程师”也明确提出了要掌握Node.js,所以学习Node.js成了我们前端开发者想要获得高薪,绕不过去的坎。
从现在的招聘信息来看,全栈是一个大趋势,两个前端技术相同的人,如果其中一个人懂后端开发,那么不管是他的薪酬待遇还是入职概率,都比另一个人要高的多。
当前端工程师了解服务端的开发之后,在与后台开发的沟通,将大大的提高沟通效率,从而提高开发效率。在这不得不说,公司真是个小机灵鬼。
所以,市场逼着我们这些前端开发者要学习服务端开发。
再从投入/回报上看
Node.js是基于JavaScript语言和V8引擎的Web服务器项目,我们可以直接使用JavaScript来搭架服务器。
快乐的是,作为前端开发,我们的看家本领就是JavaScript,学习Node简直不要太轻松,这样是大大的降低了我们学习后端开发的难度。
而且在Node环境下,通过模块化的JavaScript代码,加上函数式编程,并且无需考虑浏览器兼容性问题,直接使用最新的ECMAScript 6标准,可以完全满足工程上的需求。
在Node.js中,模块化,构建,辅助工具,调优,架构调整,这些东西是无处不在的。在Node环境中的编程思想,同时也能提高我们前端开发的质量,这样相辅相成,让我们能更快速的提高自己!
所以,学习Node无疑是快速掌握全栈技术的最好途径
悄悄的告诉你,学Node.js还有一个很大的好处,就是能更清楚的知道,服务器是怎么运作的,所以就算是做后台的,学习Node.js也是妙不可言。
2
Node.js的特点
在说Node.js解决了什么问题,如何解决痛点之前,有必要先说一下Node.js的特性
-
Node.js是一个Javascript运行环境
-
依赖于Chrome V8引擎进行代码解释
-
事件驱动
-
非阻塞I/O
-
轻量、可伸缩,适于实时数据交互应用
-
单进程,单线程
这些特点,不能一条一条的来看,而是要综合起来一起看
我认为其中有个最重要的一点,那就是事件驱动。这也是为什么Node.js采用JavaScript单线程语言,来做到非阻塞I/O,同时处理万级的并发而不会造成I/O阻塞的原因。(后面会详细说明)
其中V8引擎,是Node.js也是采用JavaScript的关键因素之一,因为它为JavaScript提供了在非浏览器运行环境,V8的高效也是导致Node.js高效的原因之一
3
Node.js解决了什么问题?
了解了Node.js的特性,再来看Node.js解决了什么问题,就更容易理解了
要学习一门新技术,那我们首先得知道他是要解决了什么别人解决不了或者是难解决的问题
不然我们一股脑的学了之后,它是用来干啥的都不知道,又怎么会知道在什么时候,应该选择什么样的技术来更优雅的写BUG呢?
Node.js主要解决了以下两个头疼的事情
-
并发连接
-
I/O阻塞
什么是并发连接?有趣的例子
并发这东西,可是吹牛必备啊,想必大家应该都知道并发是个什么玩意。但毕竟是入门文章,担心有些人没什么印象,这里还是简单提一下。
并发(Concurrency): 是指在一个系统中,拥有多个计算,这些计算有同时执行的特性,而且他们之间有着潜在的交互。
看概念很难理解,其实并不复杂,比如“我们吃饭吃到一半,电话来了,我们停了下来接了电话,接完后继续吃饭,这说明支持并发。”
用程序来解释就是一群用户同时来访问我们的网站/程序。
一般我们在处理并发的时候,都是尽可能的做到读写分离,或者分布式部署不同的服务器,或者直接简短粗暴的提升我们单机服务器的性能。
可这样做要么是苦逼了程序员,要么就是苦逼了公司的钱包。
我要是公司老板,能不出服务器这个钱就肯定不出,毕竟不管是升级服务器还是扩张服务器,那都是一笔不小的钱啊。
那就只能苦逼一下员工了咯~可我们又不想这么苦逼,这时候用Node.js就能帮我们省不少麻烦。
那Node.js又是怎样处理高并发的呢?
在这里容我先卖个关子,看完I/O阻塞之后,再来看看Node.js是怎样处理的。
什么是I/O阻塞?
先说说I/O是什么
I/O是input/output(输入/输出)
知道I/O之后,接下来的阻塞就很容易理解了。
形象的说,我在餐厅吃饭,就这一个座位,我点完餐后,坐在这享受美食。
然而这时候来了个路人甲,点餐后想坐下吃饭,而这时候我还没吃完呢,想让我让位置给你?不存在的
路人甲只能在旁边眼巴巴的看着我吃。得等我吃完,走了之后,他才能坐下来吃饭。而这等待的过程呢,就可以叫做阻塞。
这样做明显非常不合理嘛
那Node.js是怎么来解决I/O阻塞的呢?
Node.js遇到I/O事件,并不会先处理,而是先放在事件队列中,主线程依然继续往下执行,利用异步操作来处理事件队列中的事情。
作为一个干货文章,怎么能少的了图呢?没图你怎么理解?
来看看从用户请求到服务器返回数据,Node.js为了应对并发,都做了些啥玩意
我想你现在肯定是懵逼的,那继续刚才吃饭的例子从头道来~~~
我跟路人甲同时进入餐馆。老板坐在板凳上等着客人光顾。
我跟路人甲同时找到老板说“老板!我要来一份鸡腿!”,这时候老板不乐意了,文明点餐,请取号排队!
我跟路人甲先后取号坐好后(肯定是我拿的1号,路人甲拿的2号啦,毕竟主角光环在这顶着)
老板喊来了服务员小A、小B
老板吩咐“小A啊~去招待1号主角点餐入座点餐”
接着又吩咐“小B啊~去招待2号路人甲入座点餐”
吩咐完后,老板继续坐板凳上等着新的客人关顾……
此时小A、小B分别就带着我跟路人甲去入座点餐
当我或者路人甲吃完饭结账离开时,老板都会对我们说“慢走~~~谢谢惠顾,下次再来!”
故事说完,咱们来按照故事解释一下程序的运作
我跟路人甲就是用户。
老板,就是这个程序的主线程。
服务员小A,小B就是线程池里的子线程。
我跟路人甲同时找到老板点餐,这就是用户的并发请求。
老板管理整个餐厅的运作,发现我们的请求是I/O阻塞呀,所以让我们排队取号。这也就是Node.js中维护的事件队列。
我们点餐之后的入座就餐还有上菜,这些I/O阻塞的事情,都是由服务员来提供服务,老板并不参与其中。
在我们用餐完毕的时候,会通知老板我们要结账离开,结账离开这个动作就是事件执行完毕后的一个回调函数。
此时老板就会跟我们说“慢走~~~谢谢惠顾,下次再来!”,也就是事件执行完后回调函数的一个操作,并且将线程释放回线程池。
现在再回过头去看图,是不是清晰的多了。
总结以下:
1、每个Node.js进程只有一个主线程在执行程序代码。
2、当用户的网络请求或者其它的异步操作到来时,Node.js都会把它放到“事件队列”之中,并不会立即执行它,代码就不会被阻塞,主线程继续往下走,直到主线程代码执行完毕。
3、当主线程代码执行完毕完成后,通过事件循环机制,从“事件队列”的开头取出一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,一直执行到事件队列的尾部。期间主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交代码循环处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
以Java、C#为例,在Java、C#中也有办法实现并行请求(利用子线程),但NodeJS通过回调函数(Callback)和异步机制,把I/O阻塞处理的更优雅(说人话就是用Code实现起来更简单易懂,并且性能更好)。
说到这里,你可能就会问
说好的Node.js是单线程的呢?那Node.js怎么又创建子线程了呢?怎么就跟Java、C#中的线程不一样了呢?怎么就更优雅了呢?
那就来解释一波
Node.js使用异步IO和事件驱动(回调函数)来解决这个问题。
一般来说,高并发解决方案会提供多线程模型,为每个业务逻辑提供一个线程,通过系统线程切换来来弥补同步I/O调用的时间开销。
像PHP,是一个请求一个线程,假如一个线程需要2M内存,那么同时有512个请求,就需要1G的内存,那同时有5120个请求,就需要10G内存,现在内存这么贵的情况下,想想都恐怖。。。
而Node.js使用的是单线程模型,对所有I/O都采用异步的请求方式,在Node.js执行的时候维护着一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步I/O请求完成后都会被推送到事件队列中的等待执行。
Node.js是因为是基于JavaScript的特性,所以单线程的,但是,单线程只局限于JavaScript部分,Node.js核心还是用C/C++语言编写的,文章开头就说到过,它只是给JavaScript提供了一个运行环境而已,它是JavaScript语言的解释器。
我们所看到的Node.js单线程只是一个js主线程,本质上的异步操作还是由线程池完成的,Node.js将所有的阻塞操作都交给了内部的线程池去实现,本身只负责不断的往返调度,并没有进行真正的I/O操作,从而实现异步非阻塞I/O,这便是Node.js单线程和事件驱动的精髓之处了。
所以它跟Java、PHP、C#这些后台语言的实现的线程不一样的。
关于更优雅呢,主要就是实现起来更容易。比如说在Java、C#中实现多线程的时候,总是要注意线程安全,这里要Lock,那里也要Lock。
一个不注意就得加班熬夜好几个通宵才能找出问题所在……
而Node.js却是利用单线程执行,Node.js官方也是说不需要锁。但是不需要锁,不代表你可以乱来,不需要锁的是代码function,而不是你的资源! 这点要分清楚。
4
搭建我们第一个Node.js服务器
看了这么多基础性的内容,枯燥、乏味,没劲,看的想睡觉。
现在动手操作一波,刺激一下自己的肾上腺素,一起来搭建一个超简单的Node.js服务器
//引用http模块 const http=require('http'); //创建一个服务 //request:服务器接收的请求数据 //response:服务器发出的数据 let server=http.createServer((request, response)=>{ //判断接收到的url switch(request.url){ case '/hello': response.write('hi'); break; case '/word': response.write('gun'); break; case '/bb.html': response.write ('<html><head></head><body>Talk is cheap,show me the code</body></html>'); break; } //关闭 response.end(); }); //服务器监听的端口,填写端口地址时,注意端口地址不能与其它应用程序冲突 server.listen(8000);