Nodejs架构
本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Natives modules(内部核心模块)
- 当前层内容由JS实现
- 提供应用程序可直接调用库,如:fs、path、http等
- JS语言无法直接操作底层硬件设置
Builtin modules "胶水层"(找到与JS对应的C++模块)
使用js调用C++的接口
底层(构建JS代码执行环境,并执行JS代码)
- V8:执行JS代码,提供桥梁接口(js代码执行环境)
- Libuv:时间循环、时间队列、异步IO(处理代码执行的细节)
- 第三方模块:zlib、http、c-ares等
为什么是Nodejs
用户通过客户端发送请求到服务端,服务端通过请求执行相应的业务逻辑并返回,在这个过程中影响客户获取数据速度的是IO处理。 IO是计算操作过程中最缓慢的环节。
传统做法使用多线程,就容易导致多个请求出现无法响应情况
- 多线程占用内存高
- 多线程间切换使得CPU开销大
- 多线程由内存同步开销
Reactor模式,单线程完成多线程工作
Nodejs在Reactor模式下实现异步IO、事件驱动
异步: 发出操作指令,然后就可以去做别的事情了(主线程无需等待),所有操作完成后执行回调
Nodejs更适用于IO密集型高并发请求
Nodejs异步IO
执行任务一需要时间 t1,执行任务二需要时间 t2。在同步情况下完成两个任务需要时间 t1+t2,在异步情况下,执行完两个任务需要 t3 < t1+t2。
操作系统中存在,阻塞IO和非阻塞IO。当操作系统使用非阻塞IO时,系统的时间片可以用来做别的操作,但是IO操作不会返回具体的数据,只会返回IO状态。因此,操作系统会重复调用IO操作,判断IO是否结束。这种重复调用IO操作叫做轮询。 常见的轮询技术有:read、select、poll、kqueue、event ports。
libuv库是几种不同的异步IO实现方式的封装层。在运行nodejs代码,最终会在这里运行,根据当前平台进行判断、执行相应的异步IO的处理方法。
- IO是应用程序的瓶颈所在
- 异步IO提高性能无需原地等待结果返回
- IO操作数据操作系统级别,平台都有对应实现
- Nodejs单线程配合事件驱动架构以及libuv实现了异步IO
Nodejs事件驱动架构
事件驱动架构是软件开发中的通用模式
事件驱动、发布订阅、观察者
三者共同点,主体发布消息,其他实例接收消息
新建index.js文件
有一个发布者可以有多个订阅者
Nodejs单线程
单线程如何实现高并发?
异步非阻塞IO配合事件回调通知 (Nodejs主线程是单线程)
- 每个nodejs 进程只有一个主线程执行程序代码(单线程),形成一个执行栈。
- 主线程之外,还维护一个“事件队列”,当用户的网络请求或者其他异步操作的时候,node都会把操作放到事件队列中,此时并不会立即执行它,然后执行后面的代码,这时代码没有被阻塞,直到主线程代码执行完毕。
- 主线程代码执行完毕后,然后通过事件循环机制,到事件队列中取出第一个事件,然后从线程池分配一个线程去执行这个事件,然后取出第二个事件在分配一个线程去执行这个事件,然后一直循环。主线程不断的检查事件队列中是否有未执行的事件,直到所有事件执行完毕。此后每当有的事件加入到事件队列中,都会通知主线程按照顺序从事件队列中取出交给事件循环处理。当有事件执行完毕后,会通知主线程执行回调,返回结果,然后将线程交给线程池。
- 主线程重复执行第三步。
node的缺点:不擅长cpu密集型的操作
Nodejs应用场景
- IO密集型高并发请求
- 操作数据库提供API服务
- 实时聊天应用程序