严格来说,node不是一门编程语言,而是js的运行环境。
Node.js 被设计成单线程运行,但这并不意味着你无法利用到 CPU 的多个核心。你可以通过 child_process.fork() API 来生成子进程,并且它被设计成非常易于通信。而建立在同一个接口之上的 cluster 模块允许你在进程之间共享套接字(sockets),以实现核心的负载均衡。
PM2
PM2是一种基于nodejs开发的进程管理器,适用于后台常驻脚本管理,同时对node网络应用有自建负载均衡功能。最大的优点是它支持集群模式,支持负载均衡。watch模式自动重启。
安装PM2
npm i pm2 -g
PM2常用命令
pm2 start [options] 启动指定应用,等同于node [options]
pm2 stop [options] 停止指定应用
pm2 reload|restart [options] 重启指定应用
pm2 show [options] 显示指定应用详情
pm2 delete [options] 删除指定应用
pm2 kill 杀掉pm2管理的所有进程;
pm2 logs 查看指定应用的日志,即标准输出和标准错误;
pm2 monit 监控各个应用进程cpu和memory使用情况;
PM2 watch模式
pm2 start [options] --watch 启用watch模式,自动重启
PM2 cluster模式
pm2 start app.js -i max
ab压力测试
ab命令是Apache Bench的缩写,ab命令是Apache自带的压力测试工具。ab命令会创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问。它的测试目标是基于URL的,因此,它既可以用来测试Apache的负载压力,也可以测试Nginx、Lighthttp、Tomcat、IIS等其它Web服务器的压力。
ab -n 100 -c 10 http://127.0.0.1:3000/
-n 在测试会话中所执行的请求个数。默认时,仅执行一个请求。
-c 一次产生的请求个数。默认是一次一个。
Node.js文件相关
-
路径
- 变量:__dirname,__filename 文件绝对路径
- process.cwd() 命令行绝对路径 文件读写
- fs.writeFile/readFile
CSR和SSR
CSR(Client Side Rendering,客户端渲染):是一种目前流行的渲染方式,它依赖的是运行在客户端的JS,用户首次发送请求只能得到小部分的指引性HTML代码。第二次请求将会请求更多包含HTML字符串的JS文件。
SSR(Server Side Rendering,服务端渲染) :传统的渲染方式,由服务端把渲染的完整的页面吐给客户端。这样减少了一次客户端到服务端的一次http请求,加快相应速度,一般用于首屏的性能优化。
简而言之,就是数据拼接HTML字符串这件事放在服务端还是客户端造成了两者区别。
SSR | CSR | 静态 | |
---|---|---|---|
性能 | TTFB更大 | TTFB很小,但依赖大量js下载 | TTFB很小,性能最佳 |
体验 | 优,SEO友好 | 稍差,SEO不友好 | 极佳 |
技术难度 | 高 | 中 | 极低 |
TTFB(Time to First Byte,首字节时间) : 浏览器开始收到服务器响应数据的时间 = 后台处理时间 + 重定向时间,是反映服务端响应速度的重要指标
Koa模块
-
常用web开发框架
- express/koa
- Egg.js 基于koa,约定优于配置,多进程模型
- NestJS 基于express,高效可扩展的Node.js服务端框架。对ts支持比较好。
- Next.js/Nuxt.js 基于React/Vue的SSR开发框架
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。 Koa 并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助快速而愉快地编写服务端应用程序。
最简单的koa示例:
const Koa = require('koa');
const app = new Koa();
app.use(asnyc ctx => {
ctx.body = 'hello world'
});
app.listen(3000);
常用api
上下文(Context) - ctx
ctx.request
koa 的 Request 对象.注意:不等同于ctx.req,ctx.req为node的 request 对象
ctx.response
koa 的 Response 对象.同理不等同于ctx.res
Request 别名
-
以下访问器和 Request 别名等效: 即ctx.request.header等同于 ctx.header
- ctx.header
- ctx.method
- ctx.method= 带等号的代表可以赋值
- ctx.query 用于获取get请求在url里的参数
- ctx.query=
- ctx.host
- ctx.ip
- ctx.host
- 只列举了一些常用的,更多内容参考官方文档 https://koa.bootcss.com/
Response 别名
-
以下访问器和 Response 别名等效:
- ctx.body
- ctx.body=
- ctx.status
- ctx.message
- ctx.remove()
- ctx.length
koa模块 - 中间件
当一个中间件调用 next() 则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为。
流行的中间件列表:https://github.com/koajs/koa/wiki
练习一
实现一个业务中间件,打印简单版access log。js/ts均可。
要求:
不能用三方模块。
打印内容包含进程id,请求方法,路径,用户ip,referer,请求状态码,ua,请求耗时。用空格隔开。
referer没有则写 -
const Koa = require('koa');
const app = new Koa();
const main = ctx => {
ctx.response.body = 'Hello World';
console.log('进程id', process.pid)
console.log('请求方法', ctx.method)
console.log('路径', __dirname)
console.log('用户ip', ctx.ip)
if (ctx.request.referer) {
console.log('referer', ctx.request.referer)
} else {
console.log('referer' + ' ' + '-')
}
console.log('状态码', ctx.status)
console.log('ua', ctx.header['user-agent'])
console.log(ctx.response);
};
app.use(main);
app.listen(3000);
练习二
用pm2 启动koa程序
cluster模式,子进程数和CPU核心一样多
试着压测pm2和非pm2启动的性能区别
import cluster from "cluster";
import http from "http";
import { cpus } from "os";
import process from "process";
const numCPUs = cpus().length;
if (cluster.isPrimary) {
console.log(`Primary ${process.pid} is running`);
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on("exit", (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`);
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http
.createServer((req, res) => {
res.writeHead(200);
res.end("hello world\n"+process.pid+","+process.ppid);
})
.listen(8000);
console.log(`Worker ${process.pid} started`);
}
koa模块 : 路由(router)
当一个中间件调用 next() 则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为。
koa-router示例get请求:
用Koa写一个服务,实现服务端计算add,并且输出到body中
要求:
接口名:/api/add
参数缺失则赋值0
非整型需要转化
const Koa = require('koa');
const Router = require('koa-router');
// router引入、实例化的方式与koa一直
const app = new Koa();
const router = new Router();
// router编写
router.get('/', (ctx, next) => {
ctx.body = 'Hello World';
})
router.get('/test', (ctx, next) => {
ctx.body = 'Hello Test';
})
router.get('/api/add', (ctx, next) => {
// 使用ctx.query来获取get请求参数
const { x, y } = ctx.query;
const sum = parseInt(x, 10) + parseInt(y, 10);
ctx.body = { sum, ...ctx.query }
})
// 在app.use中使用router.routes()注册路由
app.use(router.routes())
app.listen(3000)
启用服务后,在浏览器输入http://localhost:3000/api/add?x=5&y=4
即可获得响应结果
koa模块 : koa-body
为什么使用koa-body?
使用原生的node对数据进行解析,操作稍显复杂,引入koa-body,可是简化代码,主要关注业务逻辑,简单地说koa-body就是用来帮助解析数据的。
koa-body与使用的请求方式无关,当请求为get时,使用ctx.query获取参数数据,当请求为post时,使用ctx.request.body/ctx.request.files搭配koa-body来获取解析的请求数据。(注意ctx.request.body中的request不能省略,因为不是别名)
koa-body的使用
(0)部分参数
koa-body参数
参数名 | 描述 | 类型 | 默认值 |
---|---|---|---|
multipart | 是否支持 multipart-formdate 的表单 | Boolean | false |
urlencoded | 是否支持urlencoded 的表单 | Boolean | true |
formidable | 配置更多的关于multipart的选项 | Object | {} |
formidable 参数
参数名 | 描述 | 类型 | 默认值 |
---|---|---|---|
uploadDir | 设置文件上传的目录 | String | os.tmpDir() |
maxFieldsSize | 限制所有字段的内存量 | Integer | 210241024 |
maxFields | 限制查询字段的数量 | Integer | 1000 |
multipart | 支持多文件上传 | Boolean | true |
(1)上传的是普通数据(除文件外的数据类型)
const Koa = require('koa')
const koaBody = require('koa-body')
const app = new Koa()
app.use(koaBody({
// 如果不加multipart:true ctx.request.body会获取不到值
multipart: true, urlencoded: true
}))
app.use((ctx) => {
const { x, y } = ctx.request.body;
const sum = parseInt(x, 10) + parseInt(y, 10)
ctx.body = { sum, ...ctx.request.body }
})
app.listen(3001)
获取数据
const data = ctx.request.body
(2)上传的是文件
const Koa = require("koa");
const koaBody = require("koa-body");
const app = new Koa();
app.use(koaBody({
// 相比普通数据,多出一个目录设置
formidable: {
uploadDir: __dirname + '/uploads'
},
multipart: true, urlencoded: true
}));
app.use((ctx) => {
// 另外使用数据为ctx.request.files
console.log("files", ctx.request.files)
ctx.body = ctx.request.body;
});
app.listen(3003);
获取数据
const data = ctx.request.files
补充:koa-body 可设置属性encoding:'gzip’来进行压缩,但不要这样做,请将压缩操作放在nginx中,js不擅长压缩/解压缩,效率较低,并且在Nginx压缩,上传到服务器和在服务器取数据时,数据都更小,效率更高。
总结: koa-body只是帮助解析post请求体的中间件,只需要在app中注册一次即可使用解析后的数据。
koa模块 : koa-static
koa-static非常简单,只需要设置一个静态目录即可访问,示例如下:
代码目录结构
├─staticDemo.js
└─static
└─image
└─test.png
// staticDemo.js
const Koa = require('koa');
const static = require('koa-static');
const app = new Koa();
//设置静态文件目录
app.use(static(__dirname + '/static/'));
app.listen(3007);
// 在浏览器输入http://localhost:3007/image/test.png即可访问static文件夹下的图片文件test.png