当前位置: 首页 > 工具软件 > koa-seo > 使用案例 >

服务端学习笔记:PM2、Node.js、Koa

堵宪
2023-12-01

严格来说,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字符串这件事放在服务端还是客户端造成了两者区别。

SSRCSR静态
性能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 的表单Booleanfalse
urlencoded是否支持urlencoded 的表单Booleantrue
formidable配置更多的关于multipart的选项Object{}

formidable 参数

参数名描述类型默认值
uploadDir设置文件上传的目录Stringos.tmpDir()
maxFieldsSize限制所有字段的内存量Integer210241024
maxFields限制查询字段的数量Integer1000
multipart支持多文件上传Booleantrue

(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
 类似资料: