NestJS 性能优化:从应用到部署的最佳实践

news2025/1/5 13:04:25

在上一篇文章中,我们介绍了 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.coloradmin.cn/o/2270140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

coredns报错plugin/forward: no nameservers found

coredns报错plugin/forward: no nameservers found并且pod无法启动 出现该报错原因 是coredns获取不到宿主机配置的dns地址 查看宿主机是否有dns地址 resolvectl status 我这里是配置正确后&#xff0c;如果没配置过以下是不会显示出dns地址的 给宿主机增加静态dns地址之后将…

2025加密风云:行业变革与未来趋势全景透视

引言 2024年是加密行业发展历程中的重要一年&#xff0c;诸多事件和趋势为未来的发展奠定了基础。随着全球政策环境的变化、技术的不断进步以及市场参与者的多样化&#xff0c;加密行业在2025年将迎来新的转型与挑战。这篇文章将从政策、技术、市场、应用以及社会影响等多个角…

logback之自定义pattern使用的转换器

目录 &#xff08;1&#xff09;场景介绍 &#xff08;2&#xff09;定义转换器BizCallerConverter &#xff08;3&#xff09;logback配置conversionRule &#xff08;4&#xff09;测试效果 前文《logback之pattern详解以及源码分析》已经介绍了pattern&#xff0c;以及…

mac m2 安装 docker

文章目录 安装1.下载安装包2.在downloads中打开3.在启动台打开打开终端验证 修改国内镜像地址小结 安装 1.下载安装包 到官网下载适配的安装包&#xff1a;https://www.docker.com/products/docker-desktop/ 2.在downloads中打开 拖过去 3.在启动台打开 选择推荐设置 …

发那科 PMC 学习与总结

1、CNC 与 PMC CNC&#xff08;Computerized Numerical Control&#xff1a;计算机控制的数控装置&#xff09;和PLC&#xff08;Programmable Logic Controller&#xff1a;可编程顺序逻辑控制器&#xff09;的各项处理由几部分构成。CNC 中系统的控制软件已安装完毕&#xf…

【机器学习应用】决策树

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;机器学习应用入门到进阶 &#x1f30f;代码仓库&#xff1a;GitHub平台 写在开头 现在您看到的是我的结论或想法&#xff0c;但在这背后凝结了大量的思考、经…

「Mac畅玩鸿蒙与硬件52」UI互动应用篇29 - 模拟火车票查询系统

本篇教程将实现一个模拟火车票查询系统&#xff0c;通过输入条件筛选车次信息&#xff0c;并展示动态筛选结果&#xff0c;学习事件处理、状态管理和界面展示的综合开发技巧。 关键词 条件筛选动态数据展示状态管理UI交互查询系统 一、功能说明 模拟火车票查询系统包含以下功…

JVM对象内存分配

1 栈上分配 栈空间随着方法执行完毕而回收通过栈上分配对象内存空间的方式&#xff0c;减少对堆空间的使用&#xff0c;从而减少gc的压力&#xff0c;提升程序性能 逃逸分析&#xff1a;分析对象的作用域&#xff0c;判断对象所需内存是否可以在栈上分配当对象没有被外部方法或…

Science Advances:一种多功能软变形和触觉显示器

01 内容概览现有技术缺点无法主动将变形的表面重新配置为各种 3D 形状。设备庞大复杂&#xff1a;机械装置依赖线性致动器和刚性结构&#xff0c;阻碍其小型化和实用性。功能单一&#xff1a;现有软材料驱动方案通常只能变形&#xff0c;缺乏多模态触觉反馈&#xff0c;难以满足…

如何从文档创建 RAG 评估数据集

我们在 Hugging Face Hub 上自动生成的 RAG 评估数据集&#xff08;来自欧盟的PDF 输入文件&#xff0c;根据CC BY 4.0许可&#xff09;。图片由作者提供 在本文中&#xff0c;我将向您展示如何创建自己的 RAG 数据集&#xff0c;该数据集包含任何语言的文档的上下文、问题和答…

flux中的缓存

1. cache&#xff0c;onBackpressureBuffer。都是缓存。cache可以将hot流的数据缓存起来。onBackpressureBuffer也是缓存&#xff0c;但是当下游消费者的处理速度比上游生产者慢时&#xff0c;上游生产的数据会被暂时存储在缓冲区中&#xff0c;防止丢失。 2. Flux.range 默认…

CES Asia 2025:科技盛宴引领未来,BESTAR声学创新备受瞩目

随着科技行业的蓬勃发展&#xff0c;亚洲消费电子技术领域的年度盛会——CES Asia 2025&#xff08;赛逸展&#xff09;即将盛大举行。作为全球消费电子行业的风向标展会&#xff0c;CES Asia 2025吸引了众多顶尖科技企业参与&#xff0c;将围绕智能家居、智能出行、虚拟现实、…

HTML-CSS-常见标签与样式

目录 一. 央视新闻排版1.1 标题1.2 正文1.3 案例1.3.1 顶部导航栏1.3.2 flex布局1.3.3 表单标签1.3.4 表单项标签1.3.5 表格 1.3 课程总结 \quad 一. 央视新闻排版 \quad \quad 1.1 标题 \quad ALTp就是用AI快速生成 标题一共有6级 \quad 1.2 正文 \quad 定义视频 定义图片 样…

c++领域展开第九幕——类和对象(下篇收尾 友元函数、内部类、拷贝对象时的编译器优化)超详细!!!!

文章目录 前言一、友元函数二、内部类三、匿名对象四、对象拷贝时的编译器优化总结 前言 上篇博客我们学习了类和对象后期的一些内容——初始化列表、类型转换、static成员 今天我们来对类和对象进行最后的收尾 开始发车啦 一、友元函数 • 友元提供了一种突破类访问限定符封装…

LevelDB 源码阅读:利用 Clang 的静态线程安全分析

LevelDB 中有一些宏比较有意思&#xff0c;平时自己写代码的时候&#xff0c;还基本没用过。这些宏在 thread_annotations.h 中定义&#xff0c;可以在编译时使用 Clang 编译器的线程安全分析工具&#xff0c;来检测潜在的线程安全问题。 比如下面这些宏&#xff0c;到底有什么…

什么是实体完整性约束?

实体完整性约束是数据库设计中的一个核心概念&#xff0c;它确保了关系数据库中每个实体的唯一性和可标识性。以下是对实体完整性约束的详细解释&#xff1a; 一、定义 实体完整性约束是指关系的主关键字&#xff08;Primary Key&#xff09;不能重复&#xff0c;也不能取“空值…

OpenCV-Python实战(15)——像素直方图均衡画

一、像素均值与标准差 1.1 像素均值 cv2.mean() mean_val cv2.mean(img,mask*) mean_val&#xff1a;图像 BGR 通道的均值和透明度。 img&#xff1a;图像。 mask&#xff1a;可以选择是否添加掩膜&#xff0c;默认为&#xff1a;None。 import cv2 import numpy as npim…

推理加速:投机采样经典方法

一 SpecInfer 基于模型 SpecInfer&#xff08;[2305.09781] SpecInfer: Accelerating Generative Large Language Model Serving with Tree-based Speculative Inference and Verification&#xff09; SpecInfer 投机采样利用多个小型模型&#xff08;SSM&#xff09;快速生…

Docker学习相关笔记,持续更新

如何推送到Docker Hub仓库 在Docker Hub新建一个仓库&#xff0c;我的用户名是 leilifengxingmw&#xff0c;我建的仓库名是 hello_world。 在本地的仓库构建镜像&#xff0c;注意要加上用户名 docker build -t leilifengxingmw/hello_world:v1 .构建好以后&#xff0c;本地会…

数据挖掘——支持向量机分类器

数据挖掘——支持向量机分类器 支持向量机最小间隔面推导基于软间隔的C-SVM非线性SVM与核变换常用核函数 支持向量机 根据统计学习理论&#xff0c;学习机器的实际风险由经验风险值和置信范围值两部分组成。而基于经验风险最小化准则的学习方法只强调了训练样本的经验风险最小…