当前位置: 首页 > article >正文

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 应用的性能优化策略:

  1. 应用层优化:路由优化、数据序列化
  2. 数据库优化:查询优化、索引设计
  3. 缓存策略:Redis 集成、多级缓存
  4. 内存管理:内存泄漏防护、垃圾回收优化
  5. 部署优化:PM2 集群、Nginx 负载均衡
  6. 监控与性能分析:Prometheus 集成、日志优化

通过实施这些优化策略,我们可以显著提升 NestJS 应用的性能和可靠性。在实际应用中,建议根据具体场景和需求选择合适的优化方案。

如果觉得这篇文章对你有帮助,别忘了点个赞 👍


http://www.kler.cn/a/463074.html

相关文章:

  • 数据库进阶教程之DDL语句(万字详解)
  • 计算机网络•自顶向下方法:网络层介绍、路由器的组成
  • docker使用国内镜像
  • 华为ensp-BGP路由过滤
  • Java重要面试名词整理(十八):Sentinel
  • K8s高可用集群之Kubernetes集群管理平台、命令补全工具、资源监控工具部署、常用命令
  • FPGA的FIFO
  • 数据挖掘——认识数据
  • SQL-Server链接服务器访问Oracle数据
  • 【蓝桥杯】:蓝桥杯之路径之谜
  • 机器人C++开源库The Robotics Library (RL)使用手册(四)
  • 关于ElasticSearch
  • 搭建医疗产品行业知识中台的手册
  • 深度学习在文本情感分析中的应用
  • 基于Redis的分布式锁
  • easybox
  • 【YashanDB知识库】hive初始化崖山报错YAS-04209
  • 万里数据库GreatSQL监控解析
  • 永嘉县瓯北六小:庆元旦,献爱心,让新永嘉人在童装节中找到归属感!
  • Golang学习历程【第五篇 复合数据类型:数组切片】
  • ShardingSphere-Proxy分表场景测试案例
  • CPT203 Software Engineering 软件工程 Pt.4 软件设计(中英双语)
  • Spring 核心技术解析【纯干货版】- II:Spring 基础模块 Spring-Beans 模块精讲
  • pyside6总结
  • 网络编程原理:回显服务器与客户端通信交互功能
  • Day20:逻辑运算