Appearance
NestJS 性能优化 (Performance Optimization)
NestJS 应用程序的性能优化是确保应用程序在高负载下保持响应性和效率的关键。本指南涵盖了各种性能优化技术和最佳实践,以帮助您构建高效的 NestJS 应用程序。
基础性能概念
NestJS 性能优化涉及:
- 启动时间优化 - 减少应用程序启动时间
- 内存管理 - 优化内存使用
- 请求处理 - 提高请求处理效率
- 数据库优化 - 优化数据访问性能
- 缓存策略 - 使用缓存提高响应速度
- 并发处理 - 提高并发处理能力
启动时间优化
模块懒加载
typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { CatsModule } from './cats/cats.module';
import { DogsModule } from './dogs/dogs.module';
@Module({
imports: [
// 使用动态导入实现懒加载
CatsModule,
DogsModule,
],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
按需导入
typescript
// 避免不必要的导入
// bad
import * as nestCommon from '@nestjs/common';
import * as nestCore from '@nestjs/core';
// good
import { Injectable, Controller, Get } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
// 只导入需要的模块
import { Repository } from 'typeorm';
Tree Shaking
typescript
// 使用按需导入减少打包体积
// bad
import { ValidationPipe } from '@nestjs/common';
// good - 更具体的导入
import { ValidationPipe } from '@nestjs/common/pipes/validation.pipe';
依赖注入优化
提供者作用域优化
typescript
// 使用 REQUEST 作用域仅在必要时
import { Injectable, Scope } from '@nestjs/common';
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
// 只在需要请求特定数据时使用
constructor() {
// 初始化请求特定的数据
}
}
// 默认使用 SINGLETON 作用域
@Injectable()
export class SingletonService {
// 适用于大多数服务
// 在整个应用程序生命周期内只创建一次实例
}
循环依赖避免
typescript
// 避免循环依赖
// bad - 循环依赖
// user.service.ts
@Injectable()
export class UserService {
constructor(private readonly postService: PostService) {}
}
// post.service.ts
@Injectable()
export class PostService {
constructor(private readonly userService: UserService) {}
}
// good - 使用 @Inject 解决循环依赖
// user.service.ts
@Injectable()
export class UserService {
constructor(
@Inject(forwardRef(() => PostService))
private readonly postService: PostService,
) {}
}
// post.service.ts
@Injectable()
export class PostService {
constructor(
@Inject(forwardRef(() => UserService))
private readonly userService: UserService,
) {}
}
控制器优化
路由优化
typescript
// 使用精确的路由匹配
@Controller('api/v1/users')
export class UsersController {
// 避免过于宽泛的通配符
@Get(':id/posts') // 好 - 具体路由
getUserPosts(@Param('id') userId: string) {
return this.postsService.findByUserId(userId);
}
// 使用正则表达式限制参数格式
@Get(':id(\\d+)') // 只匹配数字ID
findOne(@Param('id') id: number) {
return this.userService.findById(id);
}
}
参数处理优化
typescript
// 使用 DTO 进行参数验证
// create-user.dto.ts
import { IsString, IsEmail, IsNumber, Min, Max } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(2)
@MaxLength(50)
name: string;
@IsEmail()
email: string;
@IsNumber()
@Min(0)
@Max(120)
age: number;
}
// 在控制器中使用
@Controller('users')
export class UsersController {
@Post()
@UsePipes(new ValidationPipe())
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
}
数据库性能优化
查询优化
typescript
// 使用索引和查询优化
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
) {}
// 使用预加载关联数据
async findUsersWithPosts(): Promise<User[]> {
return this.usersRepository.find({
relations: ['posts'], // 预加载关联数据
select: ['id', 'name', 'email'], // 只选择需要的字段
});
}
// 使用查询构建器进行复杂查询
async findActiveUsersWithRecentPosts(): Promise<User[]> {
return this.usersRepository
.createQueryBuilder('user')
.innerJoinAndSelect('user.posts', 'post')
.where('user.isActive = :isActive', { isActive: true })
.andWhere('post.createdAt > :date', { date: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) }) // 7天内
.orderBy('post.createdAt', 'DESC')
.limit(100)
.getMany();
}
// 使用分页查询
async findUsersPaginated(page: number, limit: number): Promise<[User[], number]> {
const skip = (page - 1) * limit;
return this.usersRepository.findAndCount({
skip,
take: limit,
order: { createdAt: 'DESC' },
});
}
}
连接池配置
typescript
// 数据库连接配置优化
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: parseInt(process.env.DB_PORT, 10),
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
// 连接池配置
poolSize: 20, // 连接池大小
acquireTimeout: 60000, // 获取连接超时时间
connectionTimeout: 10000, // 连接超时时间
idleTimeout: 30000, // 空闲连接超时时间
extra: {
max: 20, // 最大连接数
min: 5, // 最小连接数
acquireTimeoutMillis: 60000,
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 10000,
keepAlive: true, // 保持连接活跃
},
}),
],
})
export class AppModule {}
缓存策略
内存缓存
typescript
// 使用 NestJS Cache Module
import { CacheModule, CACHE_MANAGER, Inject, Injectable } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { CatsService } from './cats.service';
@Module({
imports: [
CacheModule.register({
ttl: 5, // 秒
max: 100, // 最大缓存数量
}),
],
providers: [CatsService],
})
export class CatsModule {}
@Injectable()
export class CatsService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: any) {}
async getCatsWithCache(): Promise<Cat[]> {
const cacheKey = 'cats:all';
let cats = await this.cacheManager.get(cacheKey);
if (!cats) {
cats = await this.findAll(); // 从数据库获取
await this.cacheManager.set(cacheKey, cats, 60); // 缓存60秒
}
return cats;
}
async setCatsCache(cats: Cat[]): Promise<void> {
await this.cacheManager.set('cats:all', cats, 60);
}
async invalidateCatsCache(): Promise<void> {
await this.cacheManager.del('cats:all');
}
}
Redis 缓存
typescript
// Redis 缓存配置
import * as redisStore from 'cache-manager-redis-store';
@Module({
imports: [
CacheModule.register({
store: redisStore,
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT, 10),
ttl: 60, // 60秒
}),
],
})
export class AppModule {}
// 带过期时间的缓存服务
@Injectable()
export class CacheService {
constructor(@Inject(CACHE_MANAGER) private cacheManager: any) {}
async getOrSet<T>(
key: string,
getter: () => Promise<T>,
ttl: number = 60,
): Promise<T> {
let value = await this.cacheManager.get(key);
if (value === undefined) {
value = await getter();
await this.cacheManager.set(key, value, ttl);
}
return value;
}
}
自定义缓存装饰器
typescript
// 自定义缓存装饰器
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const Cached = createParamDecorator(
(ttl: number = 60, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return { cacheKey: request.url, ttl };
},
);
// 使用缓存装饰器
@Controller('cats')
export class CatsController {
@Get()
@Cached(300) // 缓存5分钟
findAll() {
return this.catsService.findAll();
}
}
拦截器性能优化
高效拦截器
typescript
// 高性能日志拦截器
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class PerformanceInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const now = Date.now();
const method = context.getClass().name + '.' + context.getHandler().name;
return next.handle().pipe(
tap(() => {
const responseTime = Date.now() - now;
if (responseTime > 1000) { // 只记录慢请求
console.log(`Slow request: ${method} took ${responseTime}ms`);
}
}),
);
}
}
// 带缓存的拦截器
@Injectable()
export class CachingInterceptor implements NestInterceptor {
private cache = new Map<string, { data: any; timestamp: number }>();
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const cacheKey = `${request.method}:${request.url}`;
const cached = this.cache.get(cacheKey);
const cacheExpiry = 5 * 60 * 1000; // 5分钟
if (cached && Date.now() - cached.timestamp < cacheExpiry) {
return of(cached.data);
}
return next.handle().pipe(
tap(data => {
this.cache.set(cacheKey, { data, timestamp: Date.now() });
}),
);
}
}
异步处理和队列
使用 Bull 队列
typescript
// 安装依赖
// npm install @nestjs/bull bull
// jobs.module.ts
import { BullModule } from '@nestjs/bull';
import { Module } from '@nestjs/common';
import { ProcessadorJobService } from './processors/job.processor';
import { JobController } from './job.controller';
@Module({
imports: [
BullModule.registerQueue({
name: 'email',
redis: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT, 10),
},
}),
],
providers: [ProcessadorJobService],
controllers: [JobController],
})
export class JobsModule {}
// job.processor.ts
import { Processor, Process } from '@nestjs/bull';
import { Job } from 'bull';
@Processor('email')
export class ProcessadorJobService {
@Process('sendMail')
async sendMail(job: Job<any>) {
const { email, template } = job.data;
// 异步处理邮件发送
await this.emailService.send(email, template);
return { success: true, jobId: job.id };
}
}
// job.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { Queue } from 'bull';
import { InjectQueue } from '@nestjs/bull';
@Controller('jobs')
export class JobController {
constructor(@InjectQueue('email') private emailQueue: Queue) {}
@Post('send-email')
async sendEmail(@Body() body: { email: string; template: string }) {
await this.emailQueue.add('sendMail', body, {
attempts: 3,
backoff: 3000,
});
return { queued: true };
}
}
内存管理
内存泄漏预防
typescript
// 避免内存泄漏的服务
@Injectable()
export class MemorySafeService implements OnApplicationShutdown {
private subscriptions = new Set<any>();
private caches = new Map<string, any>();
// 正确清理资源
onApplicationShutdown(signal?: string) {
// 清理订阅
this.subscriptions.forEach(sub => sub.unsubscribe?.());
this.subscriptions.clear();
// 清理缓存
this.caches.clear();
console.log(`Application is shutting down (${signal})`);
}
// 使用 WeakMap 避免内存泄漏
private weakMap = new WeakMap<object, any>();
storeWeakReference(obj: object, data: any) {
this.weakMap.set(obj, data);
}
getWeakReference(obj: object) {
return this.weakMap.get(obj);
}
}
流处理大文件
typescript
// 高效处理大数据流
@Injectable()
export class StreamProcessingService {
async processLargeDataset(filePath: string): Promise<any> {
const readStream = fs.createReadStream(filePath);
const writeStream = new stream.Writable({
objectMode: true,
write(chunk: any, encoding: string, callback: Function) {
// 处理数据块
processChunk(chunk);
callback();
},
});
return new Promise((resolve, reject) => {
readStream
.pipe(writeStream)
.on('finish', () => resolve('Processing complete'))
.on('error', reject);
});
}
}
服务器优化
集群模式
typescript
// cluster-app.ts
import * as cluster from 'cluster';
import * as os from 'os';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
if (cluster.isMaster) {
const numWorkers = os.cpus().length;
console.log(`Master server started on ${process.pid}`);
for (let i = 0; i < numWorkers; i++) {
cluster.fork();
}
cluster.on('online', (worker) => {
console.log(`Worker ${worker.process.pid} is online`);
});
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died with code: ${code}, and signal: ${signal}`);
console.log('Starting a new worker');
cluster.fork();
});
} else {
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log(`Worker server started on ${process.pid}`);
}
bootstrap();
}
服务器配置优化
typescript
// main.ts - 服务器性能优化配置
import { NestFactory } from '@nestjs/core';
import * as compression from 'compression';
import * as rateLimit from 'express-rate-limit';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
// 性能优化选项
bodyParser: true,
rawBody: true, // 启用原始请求体
});
// 启用压缩
app.use(compression());
// 速率限制
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100, // 限制每个IP 100个请求
}),
);
// 设置信任代理
app.set('trust proxy', true);
// 优化 JSON 解析
app.use(bodyParser.json({ limit: '10mb' }));
app.use(bodyParser.urlencoded({ extended: true, limit: '10mb' }));
await app.listen(process.env.PORT || 3000);
}
bootstrap();
监控和分析
性能监控
typescript
// performance-monitor.service.ts
@Injectable()
export class PerformanceMonitorService {
private metrics = new Map<string, { count: number; totalTime: number }>();
recordMetric(operation: string, duration: number) {
const metric = this.metrics.get(operation) || { count: 0, totalTime: 0 };
metric.count++;
metric.totalTime += duration;
this.metrics.set(operation, metric);
}
getMetrics() {
const result = {};
for (const [operation, metric] of this.metrics) {
result[operation] = {
avgTime: metric.totalTime / metric.count,
totalCount: metric.count,
totalTime: metric.totalTime,
};
}
return result;
}
resetMetrics() {
this.metrics.clear();
}
}
// 在拦截器中使用
@Injectable()
export class MetricsInterceptor implements NestInterceptor {
constructor(private performanceMonitor: PerformanceMonitorService) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
const handlerName = context.getHandler().name;
const className = context.getClass().name;
const operation = `${className}.${handlerName}`;
return next.handle().pipe(
tap(() => {
const duration = Date.now() - start;
this.performanceMonitor.recordMetric(operation, duration);
}),
);
}
}
数据库查询优化
查询性能分析
typescript
// database-performance.service.ts
@Injectable()
export class DatabasePerformanceService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
private dataSource: DataSource,
) {}
async analyzeQueryPerformance() {
// 使用 EXPLAIN 分析查询性能
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
try {
// 分析查询计划
const explainResult = await queryRunner.query(
'EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM user WHERE email = $1',
['user@example.com'],
);
console.log('Query Plan:', explainResult);
} finally {
await queryRunner.release();
}
}
// 批量操作优化
async batchUpdateUsers(updates: Array<{ id: number; data: any }>) {
// 使用事务进行批量更新
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
try {
for (const update of updates) {
await queryRunner.manager.update(User, update.id, update.data);
}
await queryRunner.commitTransaction();
} catch (error) {
await queryRunner.rollbackTransaction();
throw error;
} finally {
await queryRunner.release();
}
}
}
响应优化
响应压缩和流
typescript
// compression.interceptor.ts
@Injectable()
export class CompressionInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => {
// 根据数据大小决定是否压缩
if (data && JSON.stringify(data).length > 1024) { // 大于1KB
// 这里可以实现响应压缩逻辑
}
return data;
}),
);
}
}
// 流式响应
@Controller('stream')
export class StreamController {
@Get('large-data')
@Header('Content-Type', 'application/json')
getLargeDataStream() {
const stream = new Readable({
read() {},
});
// 模拟流式数据发送
setTimeout(() => {
stream.push(JSON.stringify({ data: 'chunk1' }) + '\n');
stream.push(JSON.stringify({ data: 'chunk2' }) + '\n');
stream.push(null); // 标记结束
}, 100);
return stream;
}
}
性能测试
基准测试
typescript
// performance.spec.ts
describe('Performance Tests', () => {
let app: INestApplication;
let service: PerformanceTestService;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
providers: [PerformanceTestService],
}).compile();
service = moduleRef.get<PerformanceTestService>(PerformanceTestService);
});
it('should handle concurrent requests efficiently', async () => {
const startTime = Date.now();
// 并发请求测试
const promises = Array.from({ length: 100 }, () =>
service.handleRequest()
);
await Promise.all(promises);
const duration = Date.now() - startTime;
// 确保100个并发请求在合理时间内完成
expect(duration).toBeLessThan(5000); // 小于5秒
});
it('should maintain consistent response times', async () => {
const responseTimes: number[] = [];
for (let i = 0; i < 10; i++) {
const start = Date.now();
await service.handleRequest();
responseTimes.push(Date.now() - start);
}
const avgResponseTime = responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length;
// 平均响应时间应小于200ms
expect(avgResponseTime).toBeLessThan(200);
});
});
性能优化最佳实践
1. 代码层面优化
typescript
// 避免不必要的计算
@Injectable()
export class OptimizedService {
private expensiveCalculationCache = new Map<string, any>();
getExpensiveResult(param: string) {
if (!this.expensiveCalculationCache.has(param)) {
const result = this.performExpensiveCalculation(param);
this.expensiveCalculationCache.set(param, result);
}
return this.expensiveCalculationCache.get(param);
}
private performExpensiveCalculation(param: string): any {
// 执行昂贵的计算
return { result: param.toUpperCase() };
}
}
2. 数据库层面优化
typescript
// 使用数据库索引
@Entity()
@Index(['email']) // 单列索引
@Index(['createdAt', 'status']) // 复合索引
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true, index: true }) // 列级索引
email: string;
@Column()
@Index() // 单列索引
status: string;
@CreateDateColumn()
@Index() // 时间戳索引
createdAt: Date;
}
3. 缓存策略优化
typescript
// 分层缓存策略
@Injectable()
export class LayeredCacheService {
private memoryCache = new Map<string, { data: any; timestamp: number }>();
private ttl = 60 * 1000; // 60秒
async getWithLayeredCache<T>(
key: string,
fetcher: () => Promise<T>,
): Promise<T> {
// 1. 检查内存缓存
const memoryCached = this.memoryCache.get(key);
if (memoryCached && Date.now() - memoryCached.timestamp < this.ttl) {
return memoryCached.data as T;
}
// 2. 检查外部缓存 (Redis等)
// 3. 调用数据获取函数
const data = await fetcher();
// 4. 更新内存缓存
this.memoryCache.set(key, { data, timestamp: Date.now() });
return data;
}
}
4. 监控和调优
typescript
// 性能监控配置
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: PerformanceInterceptor,
},
PerformanceMonitorService,
],
})
export class PerformanceModule {}
性能分析工具
使用 Node.js 性能钩子
typescript
// performance-analysis.service.ts
import * as perf_hooks from 'perf_hooks';
@Injectable()
export class PerformanceAnalysisService {
measureFunctionExecution<T>(fn: () => T, label: string): T {
const startMark = `${label}_start`;
const endMark = `${label}_end`;
perf_hooks.performance.mark(startMark);
const result = fn();
perf_hooks.performance.mark(endMark);
perf_hooks.performance.measure(label, startMark, endMark);
// 输出性能测量结果
const measures = perf_hooks.performance.getEntriesByName(label);
const measure = measures[0];
console.log(`${label} took ${measure.duration} milliseconds`);
return result;
}
}
总结
NestJS 性能优化是一个持续的过程,涉及应用程序的各个方面。通过实施这些优化策略,您可以显著提高应用程序的性能和响应能力。
性能优化的主要特点:
- 启动时间优化
- 内存管理优化
- 数据库查询优化
- 缓存策略实施
- 异步处理优化
- 服务器配置优化
- 监控和分析工具
- 性能测试和基准测试
- 代码层面优化
- 响应优化