必知Node应用性能提升及API test 接口测试

用户注意力无法超过 10s

提升性能方法

使用 多个进程

可以让单线程节点应用充分使用 CPU, 每个 CPU 内核都能并行运行代码

Node 原生 cluster

javascript 复制代码
// cluster.js
const cluster = require('cluster')
const os = require('os')

const WORKERS = Number(process.env.WORKERS || os.cpus().length)

if (cluster.isPrimary) {
  console.log(`[master] pid=${process.pid}, workers=${WORKERS}`)

  for (let i = 0; i < WORKERS; i++) cluster.fork()

  // worker 异常退出自动拉起
  cluster.on('exit', (worker, code, signal) => {
    console.error(`[master] worker ${worker.process.pid} exit code=${code} signal=${signal}, restart...`)
    cluster.fork()
  })
} else {
  require('./app') // 这里引入你的服务入口
}
javascript 复制代码
// app.js
const http = require('http')

const server = http.createServer((req, res) => {
  res.end(`hello from pid=${process.pid}\n`)
})

const PORT = Number(process.env.PORT || 3000)
server.listen(PORT, () => {
  console.log(`[worker] pid=${process.pid} listening on ${PORT}`)
})

// 优雅退出(配合 reload/restart 更稳)
process.on('SIGTERM', () => {
  server.close(() => process.exit(0))
})

启动

ini 复制代码
node cluster.js
# 或指定进程数
WORKERS=4 node cluster.js

集群

负载均衡

最常用:Nginx upstream 负载均衡(推荐)

适合:多台机器、多容器、多实例;稳定、功能全(健康检查、限流、TLS、缓存等)。

ini 复制代码
upstream api_upstream {
  least_conn;            # 也可用 round_robin(默认) / ip_hash(粘性)
  server 127.0.0.1:3001 max_fails=3 fail_timeout=10s;
  server 127.0.0.1:3002 max_fails=3 fail_timeout=10s;
  server 127.0.0.1:3003 max_fails=3 fail_timeout=10s;
}

server {
  listen 80;

  location / {
    proxy_pass http://api_upstream;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_http_version 1.1;
  }
}

水平扩展

PM2 (包含 集群模块)

Node 单进程吃不满多核,PM2 用 cluster 模式横向扩。

css 复制代码
# 启动:按 CPU 核心数拉起多进程
pm2 start app.js -i max --exec-mode cluster --name myapp

# 或指定数量
pm2 start app.js -i 4 --exec-mode cluster --name myapp

worker thread

简易线程池 worker-pool.js

kotlin 复制代码
const { Worker } = require('worker_threads')

class WorkerPool {
  constructor(size, file) {
    this.workers = []
    this.queue = []

    for (let i = 0; i < size; i++) {
      const worker = new Worker(file)
      worker.busy = false

      worker.on('message', (result) => {
        worker.busy = false
        worker.resolve(result)
        this.next()
      })

      this.workers.push(worker)
    }
  }

  run(data) {
    return new Promise((resolve) => {
      this.queue.push({ data, resolve })
      this.next()
    })
  }

  next() {
    const idle = this.workers.find(w => !w.busy)
    if (!idle || this.queue.length === 0) return

    const task = this.queue.shift()
    idle.busy = true
    idle.resolve = task.resolve
    idle.postMessage(task.data)
  }
}

module.exports = WorkerPool

worker.js(支持 postMessage)

javascript 复制代码
const { parentPort } = require('worker_threads')

function heavyCalc(n) {
  let sum = 0
  for (let i = 0; i < n * 1e7; i++) sum += i
  return sum
}

parentPort.on('message', (n) => {
  parentPort.postMessage(heavyCalc(n))
})

使用线程池

javascript 复制代码
const WorkerPool = require('./worker-pool')

const pool = new WorkerPool(4, './worker.js')

async function test() {
  const result = await pool.run(10)
  console.log(result)
}

test()

测试基础

放点 代码片段 具体 看仓库

scss 复制代码
const request = require('supertest')

const app = require('../../app')

 

describe('Test GET /launches', () => {

test('It should respond with 200 success', async() => {

await request(app)

.get('/launches')

.expect('Content-Type', /json/)

.expect(200)

})

})

 

describe('Test POST /launches', () => {

const completeLaunchData = {

"mission": "USS Enterprise",

"rocket": "NCC 1701-D",

"target": "Kepler-186 f",

"launchDate": "January 4, 2028"

}

 

const launchDataWithoutLaunchDate = {

"mission": "USS Enterprise",

"rocket": "NCC 1701-D",

"target": "Kepler-186 f",

}

 

const launchInviteData = {

"mission": "USS Enterprise",

"rocket": "NCC 1701-D",

"target": "Kepler-186 f",

"launchDate": "hello"

}

 

test('It should respond with 201 created', async() => {

const response = await request(app)

.post('/launches')

.send(completeLaunchData)

.expect('Content-Type', /json/)

.expect(201)

 

const requestData = new Date(completeLaunchData.launchDate).valueOf()

const responseData = new Date(response.body.launchDate).valueOf()

expect(requestData).toBe(responseData)

})

 

test('It should catch missing required properties', async() => {

const response = await request(app)

.post('/launches')

.send(launchDataWithoutLaunchDate)

.expect('Content-Type', /json/)

.expect(400)

 

expect(response.body).toStrictEqual({

error:'Missing required launch property'

})

})

 

test('It should catch invalid dates', async() => {

const response = await request(app)

.post('/launches')

.send(launchInviteData)

.expect('Content-Type', /json/)

.expect(400)

 

expect(response.body).toStrictEqual({

error:'Invalid launch date'

})

})

})

仓库

github.com/huanhunmao/...

相关推荐
摘星编程3 小时前
OpenHarmony + RN:Placeholder文本占位
javascript·react native·react.js
a1117764 小时前
医院挂号预约系统(开源 Fastapi+vue2)
前端·vue.js·python·html5·fastapi
0思必得04 小时前
[Web自动化] Selenium处理iframe和frame
前端·爬虫·python·selenium·自动化·web自动化
摘星编程4 小时前
React Native鸿蒙:Loading加载动画效果
react native·react.js·harmonyos
行走的陀螺仪6 小时前
uni-app + Vue3编辑页/新增页面给列表页传参
前端·vue.js·uni-app
摘星编程7 小时前
React Native + OpenHarmony:Spinner旋转加载器
javascript·react native·react.js
We་ct7 小时前
LeetCode 205. 同构字符串:解题思路+代码优化全解析
前端·算法·leetcode·typescript
2301_812731417 小时前
CSS3笔记
前端·笔记·css3
ziblog7 小时前
CSS3白云飘动动画特效
前端·css·css3
越努力越幸运5087 小时前
CSS3学习之网格布局grid
前端·学习·css3