性能优化与高并发
限流算法(高频手写)
面试题:常见限流算法有哪些?区别是什么?
| 算法 | 思想 | 优点 | 缺点 |
|---|---|---|---|
| 固定窗口 | 单位时间内计数,超限拒绝 | 简单 | 窗口临界点会有 2 倍突发流量 |
| 滑动窗口 | 把窗口细分,平滑统计 | 解决临界问题 | 实现略复杂 |
| 漏桶 | 请求入桶,固定速率流出 | 平滑流量、强制匀速 | 无法应对正常突发 |
| 令牌桶 | 固定速率发令牌,有令牌才放行 | 允许一定突发 | - |
令牌桶实现(最常用):
class TokenBucket {
constructor(capacity, rate) {
this.capacity = capacity; // 桶容量
this.rate = rate; // 每秒生成令牌数
this.tokens = capacity;
this.last = Date.now();
}
tryAcquire(n = 1) {
const now = Date.now();
// 按时间差补充令牌
this.tokens = Math.min(
this.capacity,
this.tokens + ((now - this.last) / 1000) * this.rate
);
this.last = now;
if (this.tokens >= n) {
this.tokens -= n;
return true;
}
return false;
}
}
分布式限流:用 Redis + Lua 脚本保证原子性(如
INCR+EXPIRE实现固定窗口,或用redis-cell)。
缓存策略
- 多级缓存:浏览器缓存 → CDN → 网关缓存 → 应用本地缓存(如 LRU)→ Redis → DB。
- 本地缓存 vs 分布式缓存:本地快但多实例不一致;Redis 一致但有网络开销。热点小数据可本地 + Redis 组合。
- 缓存穿透/击穿/雪崩见 Redis 篇。
CPU 密集 vs IO 密集
面试题:Node 适合什么场景?CPU 密集型任务怎么办?
- Node 的事件循环 + 非阻塞 IO,擅长 IO 密集型(高并发接口、网关、BFF)。
- 不擅长 CPU 密集型(大量计算会阻塞事件循环,拖垮所有请求)。解决:
worker_threads工作线程池处理计算任务。child_process/ Cluster 多进程。- 把计算交给专门的服务(如 C++/Go/Python)。
- 大任务分片,用
setImmediate让出事件循环。
横向扩展
- Cluster / PM2 cluster 模式:利用多核,多进程共享端口(原理见 Index 多进程章节)。
- 负载均衡:Nginx / LVS / 云 LB 把流量分发到多个实例(轮询、加权、IP hash、最少连接)。
- 无状态化:应用层不存会话状态(存 Redis),才能任意水平扩展。
连接池
数据库、Redis、HTTP 都应使用连接池复用连接,避免频繁建连销毁的开销(TCP 三次握手成本)。注意合理设置 connectionLimit,过大反而压垮 DB。
性能分析手段
- 压测:
autocannon、wrk、ab测 QPS / 延迟分布(关注 P95/P99 而非平均值)。 - 诊断:
clinic doctor(CPU/内存图)、clinic flame(火焰图定位热点)、--prof+--prof-process。 - APM 监控:Prometheus + Grafana、阿里 ARMS、Sentry(错误追踪)。
- 链路追踪:OpenTelemetry,定位跨服务调用瓶颈。
常见优化清单
- 数据库加索引、消除慢查询、读写分离。
- 加缓存(Redis)减少 DB 压力。
- 接口聚合、减少请求往返;耗时操作异步化(消息队列)。
- Gzip/Brotli 压缩、静态资源走 CDN。
- 开启 HTTP keep-alive、连接池复用。
- 限流、降级、熔断保护系统(雪崩防护)。