这是我参与「第五届青训营 」伴学笔记创作活动的第2天。
课堂笔记
本堂课重点内容:
- 介绍 Node.js 的应用场景
- 介绍 Node.js 运行时结构
- 如何用编写 Http Server
- 延伸话题
课前
安装 Node.js。可以选择以下一种方式:
- 从 Node.js 官网安装 nodejs.org/en/
- Mac, Linux 环境可以使用 nvm 进行安装 github.com/nvm-sh/nvm
- NVM_NODEJS_ORG_MIRROR=npmmirror.com/mirrors/nod… nvm install 16
详细知识点介绍
01. Node.js 的应用场景(why)
-
首先要提到的是前端工程化的场景。早期ajax,jquery比较流行的时候,我们直接在页面中引入需要的ib即可。后续随着模块化、transpile逐渐成熟、需求逐渐增多,对后端能力的需求也逐渐强烈。反过来说,也正是Node.js赋予了js开发者在浏览器外运行代码的能力,加速催生了这些项目的出现。
-
第二个常见的场景是使用Node,js开发Web服务端应用,也就是后端服务。这里我们特别提一下vercel这家公司。
-
最后要再说下Electron应用,这里不只是指electron,还包括nw.js.;这个场景在较大的企业里面非常常见。
第一个场景:前端工程化
- 用于打包的工具Bundle: webpack, vite, esbuild, parcel
- 用于压缩的工具Uglify: uglify.js
- 用于语法的转换Transpile: bable.js, typescript
- 其他语言加入竟争:基于go的esbuild, parcel, prisma
- 现状:Nodejs难以替代
第二个场景:Web服务器应用
- 使用nodejs开发服务端的学习曲线平缓,开发效率较高
- 运行效率接近常见的编译语言(nodejs不需要编译环境)
- 社区生态丰富及工具链成熟(npm,V8 inspector)
- 与前端结合的场景会有优势(SSR服务端渲染)
- 现状:竞争激烈,Node.js有自己独特的优势
第三个场景:Electron跨端桌面应用
- 商业应用:vscode,slack,discord,zoom
- 大型公司内部的效率工具
- 现状:大部分场景在选型时,都值得考虑
第四个场景:Node.js在字节
- BFF应用(作为端的开发者,需要很多服务端的接口,谁需要接口谁去开发,后端的开发者不需要跟前端紧密结合)、SSR应用,举例:字节开源的框架Modern.js
- 服务端应用,举例:头条搜索,西瓜视频,懂车帝(PC站点用的nodejs)
- Electron应用:飞连,飞书(迭代更新)
- 每年新增1000+ Node.js应用
02. Node.js 运行时结构(what)
nodejs的组成图引用自:github.com/joyeecheung… and learn 2019 beijing/contributing-to-node-core.pdf
- 用户代码就是业务代码,
- nodejs里面很多都是JavaScript写的。
- 当用JavaScript解决不了时效率比较低,用一种更native的N-API是特殊的场景。底层有很多c++的代码。
- v8引擎:JavaScript Runtime,诊断调试工具(inspector)
- libuv:封装了各种操作系统API,还提供了nodejs里面核心的eventloop(事件循环),syscall(系统调用)
- zlib:做一些常见的压缩和解压缩
- c-ares:用来做DNS查询的库
- llhttp:做http的解析
- openssl:网络层面的加密解密工具
- 举例:用node-fetch发起请求时
nodejs结构的特点;
1. 异步IO
当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待,有更多内存可用,系统资源利用率比较高。
2. 单线程:
worker thread可以起独立线程,但每个线程的模型没有太大变化
-
JS单线程----实际:JS线程+uv线程池+V8任务线程池+V8 Inspector线程
-
主线程是单线程的优点:不用考虑多线程状态同步问题,也就不需要锁:同时还能比较高效地利用系统资源;
-
缺点:阻塞会产生更多负面影响-----解决办法:多进程或多线程
3. 跨平台
- 跨平台(大部分功能、api)
- Node.js跨平台+JS无需编译环境(+Web跨平台+诊断工具跨平台)
- 开发成本低(大部分场景无需担心跨平台问题),整体学习成本低
03. 编写 Http Server(how)
本节目标:
1. 安装nodejs
Mac,Linux推荐使用nvm。多版本管理。
Windows推荐nvm4w或是官方安装包。
安装慢,安装失败的情况,设置安装源
NVM_NODEJS_ORG_MIRROR=npmmirror.com/mirrors/nod… nvm install 16
推荐官方安装包
2. 编写http server+client,收发GET,POST请求
http server
Hello word
const http = require('http')
const port = 3000
const server = http.createServer((req,res)=>{
res.end('hello world')
})
server.lisen(port,()=>{
console.log('server listens on:${port}`)
}}
JSON {"echo'":"Hello"}
const server = http.createServer((req,res)=>{
const bufs=[]
req..on('data',data=>{
bufs.push(data)
})
req.on('end',{}=>{
let reqData = {}
try{
reqData = JSON.parse(Buffer.concat(bufs).toString())
}catch(err){
}
res.setHead('Content-type','application/json')
res.end(JSON.stringify({
echo:reqData.msg || 'Hello',
}))
}
})
http client
const http = require('http')
const body = JSON.stringify({msg:'hello from my client'})
const req = http.request('http://127.0.0.1:3000',{
method:"POST",
header:{
'Content-type':'application/json',
'Content-Length':body.length,
},
},(res)=>{
const bufs = []
res.on('data',data =>{
bufs.push(data)
})
res.on('end',()=>{
const receive = JSON.parse(Buffer.concat(bufs(.toString())
console.log('receive:',receive
})
})
req.end(body)
promisify
- 用Promise+async await重写这两个例子(why)
- 技巧:将callback转换成promise
3. 编写静态文件服务器
编写一个简单的静态文件服务
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');
const port = 3000
const server = http.createServer((req,res)=>{
const info = url.parse(req.url)
const file = fs.createReadStream(path.resolve(_dirname,'.'+info.pathname))
file.pipe(res)
})
server.listen(port,()=>{
console.log('server listens on:${port}')
})
与高性能、可靠的服务相比,还差什么?
- CDN:缓存+加速
- 分布式储存,容灾
外部服务:cloudflare,七牛云,阿里云,火山云(字节跳动)...
4. 编写react SSR服务
SSR(server side rendering)有什么特点?
- 相比传统HTML模版引擎:避免重复编写代码
- 相比SPA(single page application):首屏渲染更快,SEO友好 缺点: 通常qps较低,前端代码编写时需要考虑服务端渲染情况
HTML例子:
替换成React:
SSR难点:
-
需要处理打包代码
Js tmp.js require('./static/style.css')
-
需要思考前端代码在服务端运行时的逻辑
async componentDidMount() const res=await fetch('my.server.domain')
-
移除对服务端无意义的副作用,或重置环境
5. 适用inspector进行debug、诊断
V8 Inspector:开箱即用、特性丰富强大、与前端开发一致、跨平台
- node --inspect
- open http://localhost:9229/json
场景:
- 查看console.log内容
- breakpoint
- 高CPU、死循环:cpuprofile
- 高内存占用:heapsnapshot
- 性能分析
演示
6. 部署简介
部署要解决的问题
- 守护进程:当进程退出时,重新拉起
- 多进程:cluster便捷地利用多进程
- 记录进程状态,用于诊断 容器环境
- 通常有健康检查的手段,只需考虑多核cpu利用率即可
04. 延伸话题
nodejs贡献代码
快速了解Node.js代码
- [Node.js Core贡献入门](talks/contributing-to-node-core.pdf at master · joyeecheung/talks (github.com))
好处:
- 从使用者的角色逐步理解底层细节,可以解决更复杂的问题;
- 自我证明,有助于职业发展:
- 解决社区问题,促进社区发展 难点:
- 花时间
编译nodejs
为什么要学习编译Node.js
- 认知:黑盒到白盒,发生问题时能有迹可循
- 贡献代码的第一步 如何编译
- 参考:Maintaining the build files
- /configure &&make install
- 演示:给net模块添加自定义属性
课后
- 了解并尝试使用更多 Node.js 的原生模块 nodejs.org/dist/latest…
- 学习在 npm 上搜索并安装模块 www.npmjs.com/