Node.js的单线程并不是真正的单线程,
只是开启了单个线程进行业务处理(cpu的运算),
同时开启了其他线程专门处理I/O
首先明确一点:
绝对不能让node做太多的业务逻辑,他只适合接收生成好的数据,然后或渲染后,或直接发送到客户端。
Node使用事件驱动,非阻塞I/O
它实现了诸如文件系统、模块、包、操作系统 API、网络通信等Core JavaScript没有或者不完善的功能。历史上将JavaScript移植到浏览器外的计划不止一个,但Node.js 是最出色的一个。
(零)node.js的并发处理和传统java、php的并发处理?
node.js的并发处理是万级别的,而传统的java,php是千级别的。
因为node.js他是单线程多io线程,而java、php是每来一个链接就创建一个新的线程,而且线程是需要内存的,java、php每一个线程大概需要2m内存,8g内存条大概一台机器能处理4000个链接,并且这种并发操作还是要锁住状态滴。。因此java、php这种语言的最大链接数取决于cpu的内存,
node.js虽然是单线程的,但他却使用非阻塞异步io,使得其处理链接是万级别的。。因为主线程只负责处理请求,io由线程池解决,因此node.js适合处理数据密集(io非阻塞)。但是他不适合做大量业务的处理(大量使用到cpu,他还只能利用一个cpu,累死)。。。。。。。。。。。。。。。。
总结:
java适合处理业务密集,其线程并发数取决于内存大小
node.js适合处理数据密集,因为其io非阻塞,其处理请求量是万级的。。。。
(一)事件驱动编程
Node使用的却是事件驱动编程的思想。那什么是事件驱动编程。
事件驱动编程,为需要处理的事件编写相应的事件处理程序。代码在事件发生时执行。
为需要处理的事件编写相应的事件处理程序。要理解事件驱动和程序,就需要与非事件驱动的程序进行比较。实际上,现代的程序大多是事件驱动的,比如多线程的程序,肯定是事件驱动的。早期则存在许多非事件驱动的程序,这样的程序,在需要等待某个条件触发时,会不断地检查这个条件,直到条件满足,这是很浪费cpu时间的。而事件驱动的程序,则有机会释放cpu从而进入睡眠态(注意是有机会,当然程序也可自行决定不释放cpu),当事件触发时被操作系统唤醒,这样就能更加有效地使用cpu。
来看一张简单的事件驱动模型(uml):
事件驱动模型主要包含3个对象:事件源、事件和事件处理程序。
事件源:产生事件的地方(html元素)
事件:点击/鼠标操作/键盘操作等等
事件对象:当某个事件发生时,可能会产生一个事件对象,该时间对象会封装好该时间的信息,传递给事件处理程序
事件处理程序:响应用户事件的代码
的存在,也没有线程上下文切换所带来的性能上的开销”,那么一个线程一次只能处理一个请求岂不是无稽之谈,先让我们看张图:
Node.js的单线程并不是真正的单线程,只是开启了单个线程进行业务处理(cpu的运算),同时开启了其他线程专门处理I/O。当一个指令到达主线程,主线程发现有I/O之后,直接把这个事件传给I/O线程,不会等待I/O结束后,再去处理下面的业务,而是拿到一个状态后立即往下走,这就是“单线程”、“异步I/O”。
I/O操作完之后呢?Node.js的I/O 处理完之后会有一个回调事件,这个事件会放在一个事件处理队列里头,在进程启动时node会创建一个类似于While(true)的循环,它的每一次轮询都会去查看是否有事件需要处理,是否有事件关联的回调函数需要处理,如果有就处理,然后加入下一个轮询,如果没有就退出进程,这就是所谓的“事件驱动”。这也从Node的角度解释了什么是”事件驱动”。
(二)node .js和java的并发处理的区别
先说nodejs和java对于并发处理上到区别。
java是最熟悉的了,所有人都知道,java在处理并发业务的时候是直接开一个线程去做。如下图所示
即使新开了线程,在并发更高的情况下,还是会出现等待,这是必然的。多个线程的并行运行,cpu会不断的在线程的上下文之间切换,会增加服务器的负担,多线程的创建和删除也会产生内存上的负担和GC的负担。当一个线程去完成一个业务的时候如果遇到IO操作还是会让线程进行阻塞,也就是说拜托不了任务同步的问题。
但是nodejs的处理方式是孑然不同,因为它的特点是单线程,异步IO,事件循环。
先说nodejs的单线程。关于它的单线程,有一个最直观的理解就是开发者不能自己新建线程去处理一个任务,对于web应用所有的请求的处理都是在一个主线程中完成的,所有请求的处理和请求的响应都是在一个线程中。这样的话当前的cpu只对当前的任务进行处理,不会同时处理多个请求,自然也就不需要像java那样需要锁,加锁,解锁,死锁各种问题。但是你也不能理解成进来一个请求就处理,处理完之后再处理下一个请求,这样的话也太low了吧。这里就提到了异步io
异步io的意思就是当主线程处理请求遇到io操作的时候不会由主线程自己去完成(因为这样会造成主线程的阻塞),而是交给libuv(这个是nodejs架构中的一部分,下面会详细描述)中的线程池去完成,线程池中的线程完成之后交给事件循环,主线程通过事件循环来拿io操作的结果,然后通过回调函数来进行处理。
总结:
JAVA总结---java就是可以多线程执行模型,java可以利用cpu的多核,而node.js不可以执行,他只能使用单线程执行模型,从而浪费了多核的cpu,在处理并发的时候,java用多线程,node.js使用单线程,但是依旧是并发操作,他的并发并不是线程的并发,而是事件的io的并发,这样在io操作执行完毕之后,js从而回调函数,进而处理的到数据
java就是新开一个线程去处理并发操作,但是不能无限的开线程
node.js就只有一个线程,这个线程用于不断接受请求做出响应,而其他的io操作交给线程池来完成,线程池处理完毕交给事件循环,主线程通过事件循环来拿io操作的结果,然后通过回调函数进行处理
(三)node.js怎么提高的并发的效率?
node.js存在线程池,本质上io操作都在线程池中,但是他可以不断的接受新的请求,就会减少用户的等待,而java虽是多线程,但他必须是需要等待的,,
nodejs主线程只有在遇到io操作的时候才会去交给线程池去处理,在主线程的业务执行完之后,开始事件循环,然后通过事件循环来得到io处理的结果,然后主线程通过回调函数完成io操作之后的业务,再去事件循环找io操作的返回结果,这么一直循环下去。
那你肯定也会说nodejs的io操作还是通过多线程来完成的,怎么就提高了并发的效率了?因为nodejs可以不断的接受请求,但是不立即返回结果,请求的处理,io的操作交给线程池去处理,处理之后再返回。这样不会产生阻塞,也就是可以提高吞吐量,而不会拒绝请求或者让请求等待。
(四)node.js的组成
v8 engine:主要有两个作用 1.虚拟机的功能,执行js代码(自己的代码,第三方的代码和native modules的代码)。
2.提供C++函数接口,为nodejs提供v8初始化,创建context,scope等。
libuv:它是基于事件驱动的异步IO模型库,我们的js代码发出请求,最终由libuv完成,而我们所设置的回调函数则是在libuv触发。
builtin modules:它是由C++代码写成各类模块,包含了crypto,zlib, file stream etc 基础功能。(v8提供了函数接口,libuv提供异步IO模型库,以及一些nodejs函数,为builtin modules提供服务)。
native modules:它是由js写成,提供我们应用程序调用的库,同时这些模块又依赖builtin modules来获取相应的服务支持
(六)node.js的执行图