- 这是我参与第五届青训营伴学笔记创作活动的第8天
Node.js的应用场景
- 前端工程化
- Bundle:webpack vite esbuild parcel
- Uglify: uglifyjs
- Transpile: bablejs typescript
- 其他语言加入竞争:esbuild parcel prisma
- 难以替代
- Web服务端应用
- 效率接近常见编译语言
- 生态丰富 npm V8-inspector
- 结合前端优势SSR
- 有独特优势
-
Electron跨端桌面应用
- vscode slack dscord zoom
- 大型公司效率化工具
- 大部分场景值得考虑
-
Nodejs在字节
- BFF SSR应用: Modern.js
- 服务端: 头条 西瓜 懂车
- Electron: 飞连 飞书
Node.js运行时结构
- V8: JavaScript Runtime, 诊断调试工具inspector
- libuv: eventloop事件循环 syscall系统调用
特点
- 异步I/O
- 单线程
- 不用考虑多线程同步 高效利用系统资源
- 阻塞产生负面影响
- 跨平台
- 开发成本低 (大部分功能 api)
编写Http Server
安装Nodejs
NVM_NODEJS_ORG_MIRROR = https://npmmirror.com/mirrors/node nvm install 16
编写Http Server + Client 收发Get Post请求
- http server
// 1. 通过 http.createServer 创建一个 http 服务
// 2. 通过 req.on('data') 接收请求体
// 3. 通过 req.on('end') 接收请求体结束
// 4. 通过 Buffer.concat(bufs).toString('utf8') 将请求体转换为字符串
// 5. 通过 JSON.parse(buf) 将请求体转换为对象
// 6. 通过 res.setHeader 设置响应头
// 7. 通过 res.end 发送响应
//
const http = require('http')
const server = http.createServer((req, res) => {
const bufs = []
req.on('data', (buf) => {
bufs.push(buf)
})
req.on('end', () => {
const buf = Buffer.concat(bufs).toString('utf8')
let msg = 'Hello'
try {
const ret = JSON.parse(buf)
msg = ret.msg
} catch (e) {
}
const responseJson = {
msg: `received: ${msg}`
}
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify(responseJson))
})
})
const port = 3000
server.listen(port, () => {
console.log(`Server is running on port ${port}`)
})
- http client
// 1. 引入http模块
// 2. 创建一个客户端
// 3. 设置请求头
// 4. 发送请求
// 5. 接收响应
// 6. 打印响应内容
//
// 1. 通过node http_server.js启动服务
// 2. 通过node http_client.js启动客户端
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'
}
}, (res) => {
const bufs = []
res.on('data', (buf) => {
bufs.push(buf)
})
res.on('end', () => {
const buf = Buffer.concat(bufs)
const json = JSON.parse(buf.toString('utf8'))
console.log(json)
})
})
req.end(body)
编写静态文件服务器
- static server
// 创建一个文件注释头
// 1. 通过http模块创建一个http服务
// 2. 通过fs模块读取文件
// 3. 通过path模块获取文件路径
// 4. 通过url模块获取请求的url
// 5. 通过stream风格的api来读取文件,减少内存占用
var http = require('http');
var fs = require('fs');
var path = require('path');
var url = require('url');
const folderPath = path.join(__dirname, './static')
var server = http.createServer((req,res) => {
const info = url.parse(req.url)
// static/index.html
const filePath = path.resolve(folderPath, './'+ info.path)
console.log(filePath);
//stream风格api可以帮助我们占用尽可能少的内存空间
const filestream = fs.createReadStream(filePath, {autoClose:true})
filestream.pipe(res)
})
const port = 3001
server.listen(port, () => {
console.log(`listening on port: ${port}`)
})
编写React SSR 服务
- SSR (server side rendering) 什么特点
- 相比于传统的HTML模板引擎 避免重复编码代码
- 相比于SPA(single page application) 首屏渲染更快 SEO友好
- 缺点
- qps较低 前端代码需要考虑服务端渲染情况
- ssr_example.js
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const http = require('http');
function App(props){
return React.createElement('div', {}, props.children || 'Hello')
}
const server = http.createServer((req,res) => {
res.end(`
<!DOCTYPE html>
<head>
<title>My Application</title>
</head>
<body>
${ReactDOMServer.renderToString(
React.createElement(App, {}, 'my-app'))}
</body>
</html>
`)
})
const port = 3001
server.listen(port, () => {
console.log(`listening on port: ${port}`)
})
- SSR难点
- 需要处理打包代码
- 需要思考前端代码在后端服务端运行时的逻辑
- 移除对服务端无意义的副作用 或重置环境
使用inspector 进行调试诊断
- V8 Inspector: 开箱即用 特性丰富 与前端开发一致 跨平台
- node --inspect
- open http://localhost:9229/json
- 场景
- 查看clog内容
- breakpoint
- 高CPU 死循环:couprofiule
- 高内存占用 heapsnapshot
- 性能分析
部署简介
- 部署主要问题
- 守护进程
- 多进程
- 记录进程状态
- 容器环境
- 通常有健康检查手段 只需要考虑多核cpu利用率即可
延伸话题
- 如何贡献
- 编译nodejs
- 诊断/追踪
- WASM / NAPI