这是我参与「第五届青训营 」笔记创作活动的第13天。
一、本堂课的重点内容
- Nodejs应用场景
- Nodejs运行时结构
- 编写HTTP Server,Nodejs使用最多的场景
二、详细知识点介绍
Nodejs应用场景
前端工程化
早期的web库Ajax、JQuery,是浏览器直接加载,不太需要后端的工程化的事情。
babel、Webpack等,需要一些Nodejs的服务端能力,调用操作系统api
- Bundle:Webpack,Vite,esbuild,parcel
- Uglify:uglifyjs,压缩,转换
- Transpile:babeljs,typescript,语法转换
- 其他语言加入前端工程化:esbuild基于go实现,parcel基于rust实现,prisma前端数据库的ORM
Web服务端应用
Vercel公司开发的Nextjs,与前端结合的web服务端应用
- 学习曲线平缓,开发效率高,js实现
- 运行效率接近常见编译语言,在解释型语言中效率较高
- 社区丰富,工具链成熟(npm,V8 inspector),有的包的成熟度可能不如其他语言,nodejs调试器与devtools相同
- 与前端结合的场景有优势(SSR),服务端渲染,自动生成HTML
Electron跨端桌面应用
VSCode,slack,discord,zoom等
大型公司内部的效率工具,跨端桌面应用,开发效率高,运行稳定,资源占用多。
字节跳动内部的Nodejs应用:
- BFF应用(业务逻辑由后端实现,接口拼接裁剪由端构建)、SSR应用,Modern.js
- 服务端应用,头条搜索,西瓜视频,懂车帝等的PC站点,最上层的后端应用由Nodejs实现
- Electron应用:飞连,飞书
Nodejs运行时结构
-
nodejs内部也会使用社区代码,如acron,node-inspect。
-
用户手动安装的包在用户代码内
-
N-API提供更高性能的Native API的代码,实现由于js效率太低无法实现的代码
-
v8是nodejs运行时,诊断调试工具(inspector)
-
libuv提供操作系统api和event loop(事件循环),syscall(系统调用)
-
nghttp2提供http2相关api
-
zlib 压缩和解压缩
-
c-ares用于DNS查询
-
llhttp提供http相关api
-
OpenSSL用于网络的加解密
Nodejs特点
-
异步IO
-
setTimeout,readFile等
-
无需阻塞线程执行,无需等待异步操作,而等待异步返回时线程内存不会释放
-
节省时间,提高效率,无需频繁申请和释放内存
-
-
单线程
-
nodejs是单线程,不适合CPU密集型操作,从nodejs12开始的worker_thread可以起独立线程,但每个线程的模型没有太大变化
-
只有JS主线程是单线程,实际上有JS线程+libuv线程池+V8任务线程池+V8 inspector线程
-
异步读文件的过程由libuv的线程池实现,主线程先做其他的事,或者高CPU的操作如加解密等,也会进入libuv的线程池去做,避免主线程的阻塞
-
优点:不用考虑多线程状态同步问题,也就不需要锁;同时还能比较高效地利用系统资源;
-
缺点:阻塞会产生更多负面影响;解决办法:多进程或多线程,nodejs更多场景会使用多进程
-
-
跨平台
-
同样的代码,如socket的,大部分API是相同的,不太用考虑跨平台的问题
-
有些API也不会跨平台,如有的API是特定操作系统专用的
-
无需编译环境,开发成本低,整体学习成本低
-
三、实践练习例子
node-fetch模块发起请求
npm安装了node-fetch,用户代码中发起了node-fetch请求,在v8内执行,调用js内的http模块,进而调用C++内的http api,调用llhttp执行序列化和反序列化,调用libuv创建tcp连接。
接收到数据后,在libuv的Event loop中处理,交给llhttp解析,再层层向上返回。
编写HTTP Server
安装nodejs,*nix下使用nvm,方便多版本管理
要对Nodejs原生的模块有一定了解,以实现多样性的需求,不要完全依赖模块
使用promisify化的函数解决回调地狱的问题。
stream风格的api:const fileStream=fs.createReadStream(path);res.pipe(fileStream),可占用尽可能少的内存空间,用fs.readFile会把整个文件读到内存,需要分配足够的内存空间。而使用fs.createReadStream会按需将数据返回给client,stream发送速率由client的消费速率而定。
HTTP Server高性能、可靠的需求:CDN,缓存,加速、分布式储存,容灾
外部服务:cloudflare、七牛云、阿里云、火山云
服务端渲染 Server Side Rendering, SSR
- 相比传统HTML模板引擎,避免重复编写代码,服务端渲染引擎不再需要HTML模板引擎,均写到js里
- 相比SPA(single page application):首屏渲染更快,SEO(搜索引擎优化)友好
- 缺点:通常qps(每秒查询率)较低,前端代码编写时需要考虑服务端渲染情况
Debug
开启调试 node --inspect
访问127.0.0.1:9229/json打开websocket调试
场景:
- 查看console.log内容
- 断点,可以加入logpoint,防止阻塞,相当于插桩
- 高CPU,死循环:cpuprofile
- 高内存占用,memory面板,通过snapshot查看是否存在内存泄露问题
部署
要解决的问题:
- 守护进程:进程退出时,重新拉起
- 多进程:cluster便捷利用多进程
- 记录进程状态用于诊断
可通过容器环境部署,只需考虑多核CPU利用率即可
四、课后个人总结
本节课学习了nodejs的相关知识,对nodejs概况有了一定的了解,学习了nodejs应用场景、架构和一些编程实例。通过本节课的学习,在之前已有的知识储备上,增加了Nodejs架构的了解,学习了Nodejs相关底层库的功能,以及了解了nodejs运行时结构。
五、参考链接
talks/contributing-to-node-core.pdf at master · joyeecheung/talks (github.com)