跳到主要内容

Nodejs生产配置清单

1. 启动参数

NODE_ENV=production \
NODE_OPTIONS="--max-old-space-size=1536 --dns-result-order=ipv4first" \
UV_THREADPOOL_SIZE=16 \
node server.js
参数含义推荐值
--max-old-space-sizeV8 老生代上限 MB容器 limit × 75%
--dns-result-order=ipv4firstDNS 优先 IPv4本地 IPv6 不通时必加
UV_THREADPOOL_SIZElibuv 线程池大小16-64(IO 密集加大)
NODE_ENV环境标识production

2. 容器内配置

ENV NODE_ENV=production
ENV NODE_OPTIONS="--max-old-space-size=1536"
ENV UV_THREADPOOL_SIZE=16

# DNS 配置:避免 IPv6 优先导致超时
ENV NODE_OPTIONS="--max-old-space-size=1536 --dns-result-order=ipv4first"

3. 关键环境变量

# === 应用 ===
PORT=3000
HOST=0.0.0.0 # 容器内必须 0.0.0.0
LOG_LEVEL=info

# === Node 运行时 ===
NODE_ENV=production
NODE_OPTIONS="--max-old-space-size=1536 --dns-result-order=ipv4first"
UV_THREADPOOL_SIZE=16
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt # 自签 CA

# === 性能 ===
NODE_TLS_REJECT_UNAUTHORIZED=1 # 生产不要设 0

4. HTTP Server 配置

const server = app.listen(Number(process.env.PORT) || 3000, '0.0.0.0')

// Keep-Alive 超时(比 Nginx 大!)
server.keepAliveTimeout = 65 * 1000 // 65s > Nginx 60s
server.headersTimeout = 66 * 1000 // 比 keepAliveTimeout 大
server.requestTimeout = 30 * 1000 // 请求超时

// 最大连接
server.maxConnections = 1000

// 最大 header 大小(防大 cookie 攻击)
server.maxHeadersCount = 100

keepAliveTimeout 必须大于 Nginx/LB 的 keepalive_timeout,否则经典 502。

5. 数据库连接池

// pg 池
const { Pool } = require('pg')
const pool = new Pool({
max: 20, // 最大连接数
min: 5, // 最小保持
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 5000,
// 超时报错而非挂起
})

// 优雅退出时关池
process.on('SIGTERM', async () => {
await pool.end()
process.exit(0)
})

K8s 多 Pod 时总连接 = Pod 数 × max。数据库有连接上限要协调。

6. Redis 配置

const Redis = require('ioredis')
const redis = new Redis({
host: process.env.REDIS_HOST,
port: 6379,
password: process.env.REDIS_PASSWORD,
maxRetriesPerRequest: 3,
retryStrategy: (times) => Math.min(times * 50, 2000),
enableReadyCheck: true,
lazyConnect: true,
})

7. 错误处理

// 未捕获异常
process.on('uncaughtException', (err) => {
logger.fatal(err, '未捕获异常')
// 记录后安全退出(让 K8s 重启)
process.exit(1)
})

// 未处理 Promise rejection
process.on('unhandledRejection', (reason) => {
logger.error({ reason }, '未处理 rejection')
// Node 15+ 默认会退出
})

不要在 uncaughtException 里继续跑——状态可能已损坏。记日志然后退出。

8. 信号处理

let shuttingDown = false

async function shutdown(signal) {
if (shuttingDown) return
shuttingDown = true
logger.info(`收到 ${signal}`)

server.close()
await pool.end()
await redis.quit()
process.exit(0)
}

process.on('SIGTERM', () => shutdown('SIGTERM'))
process.on('SIGINT', () => shutdown('SIGINT'))

9. 安全配置

const helmet = require('helmet')
app.use(helmet()) // 安全响应头

// 限流
const rateLimit = require('express-rate-limit')
app.use('/api/', rateLimit({
windowMs: 60 * 1000,
max: 100,
standardHeaders: true,
}))

// body 限制
app.use(express.json({ limit: '1mb' }))
app.use(express.urlencoded({ limit: '1mb', extended: true }))

// CORS
const cors = require('cors')
app.use(cors({
origin: ['https://app.example.com'],
credentials: true,
}))

// trust proxy(Nginx / LB 后面必须)
app.set('trust proxy', 1)

10. 性能监控

// 暴露 metrics
const client = require('prom-client')
client.collectDefaultMetrics()

// 事件循环延迟
const lag = new client.Histogram({
name: 'nodejs_eventloop_lag_seconds',
help: 'Event loop lag',
buckets: [0.001, 0.01, 0.05, 0.1, 0.5, 1],
})

const lagInterval = setInterval(() => {
const start = process.hrtime.bigint()
setImmediate(() => {
const ms = Number(process.hrtime.bigint() - start) / 1e9
lag.observe(ms)
})
}, 1000)
lagInterval.unref()

11. 完整生产启动脚本

#!/usr/bin/env bash
set -euo pipefail

export NODE_ENV=production
export NODE_OPTIONS="--max-old-space-size=${MAX_MEMORY:-1536} --dns-result-order=ipv4first"
export UV_THREADPOOL_SIZE=${THREAD_POOL:-16}

exec node dist/server.js

Dockerfile:

ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "dist/server.js"]

12. Checklist

生产 Node 应用上线前:

  • NODE_ENV=production
  • --max-old-space-size < 容器 memory limit × 75%
  • server.keepAliveTimeout > 代理 keepalive_timeout
  • trust proxy 已设
  • 优雅退出实现(SIGTERM)
  • 连接池有限制 + 关闭逻辑
  • 日志结构化 + 级别可控
  • uncaughtException / unhandledRejection 有处理
  • helmet 安全头
  • rate limit
  • body size 限制
  • 监控指标暴露

13. 延伸阅读