NestJS 性能优化:从应用到部署的最佳实践
在上一篇文章中,我们介绍了 NestJS 的微服务架构实现。本文将深入探讨 NestJS 应用的性能优化策略,从应用层到部署层面提供全方位的优化指南。
应用层优化
1. 路由优化
// src/modules/users/users.controller.ts
import { Controller, Get, UseInterceptors, CacheInterceptor } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
@UseInterceptors(CacheInterceptor)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
// 使用路由参数而不是查询参数,提高路由匹配效率
@Get(':id')
async findOne(@Param('id') id: string) {
return this.usersService.findOne(id);
}
// 避免深层嵌套路由
@Get(':id/profile')
async getProfile(@Param('id') id: string) {
return this.usersService.getProfile(id);
}
}
2. 数据序列化优化
// src/interceptors/transform.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { classToPlain } from 'class-transformer';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => {
// 使用 class-transformer 进行高效的数据序列化
return classToPlain(data, {
excludeExtraneousValues: true,
enableCircularCheck: false
});
}),
);
}
}
数据库优化
1. 查询优化
// src/modules/posts/posts.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './post.entity';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private postsRepository: Repository<Post>,
) {}
async findAll(page: number, limit: number) {
// 使用分页查询
const [posts, total] = await this.postsRepository
.createQueryBuilder('post')
.leftJoinAndSelect('post.author', 'author')
// 只选择需要的字段
.select(['post.id', 'post.title', 'author.name'])
// 添加索引字段的排序
.orderBy('post.createdAt', 'DESC')
// 使用分页
.skip((page - 1) * limit)
.take(limit)
// 缓存查询结果
.cache(true)
.getManyAndCount();
return { posts, total };
}
}
2. 索引优化
// src/entities/user.entity.ts
import { Entity, Column, Index } from 'typeorm';
@Entity()
export class User {
@Column()
@Index() // 为经常查询的字段添加索引
email: string;
@Column()
@Index() // 为经常排序的字段添加索引
createdAt: Date;
// 复合索引
@Index(['firstName', 'lastName'])
@Column()
firstName: string;
@Column()
lastName: string;
}
缓存策略
1. Redis 缓存集成
// src/config/cache.config.ts
import { CacheModule } from '@nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
export const cacheConfig = {
imports: [
CacheModule.register({
store: redisStore,
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
ttl: 60 * 60, // 1 hour
max: 100, // 最大缓存数
}),
],
};
// src/modules/products/products.service.ts
import { Injectable, Inject, CACHE_MANAGER } from '@nestjs/common';
import { Cache } from 'cache-manager';
@Injectable()
export class ProductsService {
constructor(
@Inject(CACHE_MANAGER) private cacheManager: Cache,
) {}
async getProduct(id: string) {
// 先从缓存获取
let product = await this.cacheManager.get(`product:${id}`);
if (!product) {
// 缓存未命中,从数据库获取
product = await this.productRepository.findOne(id);
// 设置缓存
await this.cacheManager.set(`product:${id}`, product, { ttl: 3600 });
}
return product;
}
}
2. 多级缓存策略
// src/services/cache.service.ts
import { Injectable } from '@nestjs/common';
import { Cache } from 'cache-manager';
import * as NodeCache from 'node-cache';
@Injectable()
export class CacheService {
private localCache: NodeCache;
constructor(
@Inject(CACHE_MANAGER) private redisCache: Cache,
) {
this.localCache = new NodeCache({
stdTTL: 60, // 本地缓存 1 分钟
checkperiod: 120,
});
}
async get(key: string) {
// 1. 检查本地缓存
let data = this.localCache.get(key);
if (data) return data;
// 2. 检查 Redis 缓存
data = await this.redisCache.get(key);
if (data) {
// 将数据放入本地缓存
this.localCache.set(key, data);
return data;
}
return null;
}
async set(key: string, value: any, ttl?: number) {
// 同时更新本地缓存和 Redis 缓存
this.localCache.set(key, value, ttl);
await this.redisCache.set(key, value, { ttl });
}
}
内存管理
1. 内存泄漏防护
// src/decorators/memory-safe.decorator.ts
import { applyDecorators, SetMetadata } from '@nestjs/common';
export function MemorySafe(options: { timeout?: number } = {}) {
return applyDecorators(
SetMetadata('memory-safe', true),
SetMetadata('timeout', options.timeout || 30000),
);
}
// src/interceptors/memory-guard.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { timeout } from 'rxjs/operators';
@Injectable()
export class MemoryGuardInterceptor implements NestInterceptor {
constructor(private reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const isMemorySafe = this.reflector.get('memory-safe', context.getHandler());
const timeoutValue = this.reflector.get('timeout', context.getHandler());
if (isMemorySafe) {
return next.handle().pipe(
timeout(timeoutValue),
);
}
return next.handle();
}
}
2. 垃圾回收优化
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as v8 from 'v8';
async function bootstrap() {
// 配置垃圾回收
v8.setFlagsFromString('--max_old_space_size=4096');
const app = await NestFactory.create(AppModule);
// 定期强制垃圾回收
setInterval(() => {
global.gc();
}, 30000);
await app.listen(3000);
}
bootstrap();
部署优化
1. PM2 集群配置
// ecosystem.config.js
module.exports = {
apps: [{
name: 'nestjs-app',
script: 'dist/main.js',
instances: 'max', // 根据 CPU 核心数自动设置实例数
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
},
}],
};
2. Nginx 负载均衡
# nginx.conf
upstream nestjs_upstream {
least_conn; # 最少连接数负载均衡
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
keepalive 32; # 保持连接数
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nestjs_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
# 开启 gzip 压缩
gzip on;
gzip_types text/plain application/json;
gzip_min_length 1000;
# 客户端缓存设置
add_header Cache-Control "public, max-age=3600";
}
}
监控与性能分析
1. 性能监控集成
// src/modules/monitoring/monitoring.service.ts
import { Injectable } from '@nestjs/common';
import * as prometheus from 'prom-client';
@Injectable()
export class MonitoringService {
private readonly requestDuration: prometheus.Histogram;
private readonly activeConnections: prometheus.Gauge;
constructor() {
// 初始化 Prometheus 指标
this.requestDuration = new prometheus.Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status'],
buckets: [0.1, 0.5, 1, 2, 5],
});
this.activeConnections = new prometheus.Gauge({
name: 'active_connections',
help: 'Number of active connections',
});
}
recordRequestDuration(method: string, route: string, status: number, duration: number) {
this.requestDuration.labels(method, route, status.toString()).observe(duration);
}
incrementConnections() {
this.activeConnections.inc();
}
decrementConnections() {
this.activeConnections.dec();
}
}
2. 日志优化
// src/modules/logging/winston.config.ts
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
export const winstonConfig = {
imports: [
WinstonModule.forRoot({
transports: [
// 控制台日志
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp(),
winston.format.colorize(),
winston.format.simple(),
),
}),
// 文件日志(按日期轮转)
new winston.transports.DailyRotateFile({
filename: 'logs/application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '14d',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
),
}),
],
}),
],
};
写在最后
本文详细介绍了 NestJS 应用的性能优化策略:
- 应用层优化:路由优化、数据序列化
- 数据库优化:查询优化、索引设计
- 缓存策略:Redis 集成、多级缓存
- 内存管理:内存泄漏防护、垃圾回收优化
- 部署优化:PM2 集群、Nginx 负载均衡
- 监控与性能分析:Prometheus 集成、日志优化
通过实施这些优化策略,我们可以显著提升 NestJS 应用的性能和可靠性。在实际应用中,建议根据具体场景和需求选择合适的优化方案。
如果觉得这篇文章对你有帮助,别忘了点个赞 👍