这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
一、本堂课重点内容:
本堂课的知识要点有哪些?
- Node.js的应用场景
- Node.js的运行结构
- 编写Http Server
- 延伸话题
二、详细知识点介绍:
Node.js的应用场景
Node.js是JavaScript语言的服务器运行环境
- Node.js 就是运行在服务端的 JavaScript。
- Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
- Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
Node.js的产生
Node.js的产生可追溯到服务器的阻塞问题:
传统服务器每次产生一个请求,就对应着一个线程。 那么就有一个问题:请求的速度由用户决定,响应的速度也可以通过提升带宽等方法比较容易的提升速度。但是I/O的输入输出速度是比较难提升的。而每一个请求就会产生一个线程,数据请求又比较慢就会出现有很多线程再缓存中等待,就会造成大量的内存浪费。所以使用Node.js编写的服务器采用单线程模式,即不管产生多少请求就只有一个线程,这样就可以节省空间,大大降低了成本。
我们常说JS是单线程的,这也与它运行在Node.js上有关。
Node.js特点
-
Node.js采用Google开发的V8引擎运行js代码,使用事件驱动、非阻塞和异步模型等技术来提高性能,可优化应用程序的传输量和规模。
-
Node.js是基于V8引擎,V8是Google发布的开源JavaScript引擎,本身就是用于Chrome浏览器的js解释部分,但是Ryan Dahl鬼才般把这个V8搬到了服务器上,用于做服务器的软件。
-
Node.js大部分基本模块都用JavaScript编写。在Node.js出现之前,JS通常作为客户端程序设计语言使用,以JS写出的程序常在用户的浏览器上运行。
-
Node.js的单线程是Node.js的一个最大的优点,但是同时也是它的缺点。
-
因为毕竟是单线程,如果请求太多会处理不过来的,所以我们一般情况下采用分布式的方法来弥补这个缺点。分布式是指采取多个服务器,Node.js对服务器的要求不高,所以不必担心价格问题。
应用场景
-
前端工程化
-
早期ajax、jquery比较流行,直接在页面中引入lib即可
-
后续模块化、transpile逐渐成熟、需求逐渐增多,对后端能力需求逐渐强烈(在浏览器外运行代码的需求)
-
Bundle: webpack, vite, esbuild, parcel
-
Uglify: uglifyjs
-
Transpile: bablejs, typescript
-
其他语言加入竞争:esbuild, parcel, prisma
-
Node.js必不可缺,难以替代
-
-
Web服务端应用
Node.js可以做到其他传统后端语言做到的事情
- 学习曲线平缓,开发效率较高
- 运行效率接近常见的编译语言
- 社区生态丰富及工具链成熟
- 与前端结合的场景会有优势
- 现状:竞争激烈,Node.js有自己独特的优势
- 例如:头条搜索,西瓜视频,懂车帝
-
Electron 跨桌面应用
- 商业应用:vscode, slack, discord, zoom
- 大型公司内的效率工具
- 现状:大部分场景在选型时,都值得考虑
-
Node.js在字节
-
BFF应用、SSR应用,举例:Modern.js
-
服务端应用,例如:头条搜索,西瓜视频,懂车帝
-
Electron应用:飞连,飞书
-
每年新增1000+ Node.js应用
-
Node.js运行结构
- V8: JS Runtime, 诊断调试工具(inspector)
- libuv:eventloop(事件循环)syscall(系统调用)
- 和浏览器相比node.js环境中是没有Dom和Bom的
- 出于安全问题考虑,浏览器不支持跨域请求和文件读写功能;
而Node.js没有安全限制可以直接访问到终端提供的很多的API方法:
- 可以直接操作文件系统
- 进行进程管理(解决单线程问题,在Node下可以创建多个进程)
- 可以进行跨域请求
特点
- 异步I/O:
当Node.js执行I/O操作时,会在响应返回后恢复操作,而不是阻塞线程并占用额外内存等待
- 单线程:
JS线程 + uv线程池 + V8任务线程池 + V8 Inspector线程
优点:不用考虑多线程状态同步问题,也就不需要锁;同时比较高效地利用系统资源
缺点:阻塞会产生更多负面影响
解决办法:多进程或多线程
- 跨平台:
跨平台(大部分功能、api)
Node.js跨平台+JS无需编译环境(+ Web 跨平台+诊断工具跨平台)
开发成本低(大部分场景无需担心跨平台问题),整体学习成本低
编写Http Server
安装 Node.js可以选择以下一种方式:
-
从 Node.js 官网安装 nodejs.org/en/
-
Mac, Linux 环境可以使用 nvm 进行安装 github.com/nvm-sh/nvm
实战示例
Hello World!
const http =require('http')//导入Http模块
const port = 3000
const server = http.createServer((req,res)=>{ // 创建web服务器实例;每次接到回调请求时回调函数就被触发
res.end("hello")
})
server.listen(port,()=>{
console.log('server listens on: ${port}')
})
JSON
const server = http.createServer((reg,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){
// receive invalid json data
}
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({
echo: reqData.msg || 'Hello",
))
})
})
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',
headers: {
'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.contact(bufs).toString())
console.log('receive:', receive)
})
})
req.end(body)
通过promise+async await重写例子
将callback转换成promise
function wait(t) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, t)
})
}
wait(1000).then(() => { console.log('get called') })
静态文件服务
stream api好处:占用尽可能少的内存空间,内存使用率更高
缓存+加速,分布式储存,容灾,外部服务
static_file_server
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}')
})
React SSR
server side rendering
- 相比HTML模版引擎:避免重复编写代码
- 相比Single Page Application:首屏渲染更快,SEO友好
- 缺点:通常qps较低,前端代码编写时需要考虑服务端渲染情况
SSR难点:
1.需要处理打包代码
2.需要思考前端代码在服务端运行时的逻辑
3.移除对服务器无意义的副作用,或重置环境
Debug
V8 Inspector:开箱即用、特性丰富强大、与前端开发一致、跨平台
-
node --inspect
场景:
- 查看console.log内容
- breakpoint
- 高CPU、死循环:cpuprofile
- 高内存占用:httpsnapshot
- 性能分析
部署
- 守护进程:当进程退出时,重新拉起
- 多进程:cluster便捷地利用多进程
- 记录进程状态,用于诊断
容器环境:通常有健康检查的手段,只需考虑多核CPU利用率即可
延伸话题
Node.js贡献代码
快速了解Node.js代码
好处
- 从使用者的角色逐步理解底层细节,可以解决更复杂的问题
- 自我证明,有助于职业发展
- 解决社区问题,促进社区发展
诊断/追踪
- 一个低频,重要,也相当有挑战的方向,是企业衡量自己能否依赖一门语言的重要参考
- 技术咨询行业中的热门角色
难点: 需要了解Node.js底层,需要了解操作系统以及各种工作 ,更需要经验
WASM,NAPI
Node.js是执行WASM代码的天然容器,WebAssembly/wasm WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。
NAPI是执行C接口的代码(C/C++/RUS...),同时还能保留原生代码的性能