NestJS 微服务架构:从单体到分布式

在上一篇文章中,我们介绍了 NestJS 的请求处理流程。本文将深入探讨如何使用 NestJS 构建微服务架构,实现从单体应用到分布式系统的转变。

微服务基础配置

1. 安装依赖

bash 复制代码
# 安装微服务相关依赖
npm install @nestjs/microservices
# 安装消息队列依赖
npm install amqplib @nestjs/microservices @golevelup/nestjs-rabbitmq
# 安装 gRPC 依赖
npm install @grpc/grpc-js @grpc/proto-loader

2. 微服务配置

typescript 复制代码
// src/config/microservice.config.ts
import { Transport } from '@nestjs/microservices';

export const microserviceConfig = {
  // RabbitMQ 配置
  rabbitmq: {
    transport: Transport.RMQ,
    options: {
      urls: [process.env.RABBITMQ_URL || 'amqp://localhost:5672'],
      queue: 'main_queue',
      queueOptions: {
        durable: true,
      },
    },
  },

  // gRPC 配置
  grpc: {
    transport: Transport.GRPC,
    options: {
      package: 'hero',
      protoPath: join(__dirname, '../proto/hero.proto'),
      url: 'localhost:5000',
    },
  },

  // TCP 配置
  tcp: {
    transport: Transport.TCP,
    options: {
      host: 'localhost',
      port: 4000,
    },
  },
};

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions } from '@nestjs/microservices';
import { AppModule } from './app.module';
import { microserviceConfig } from './config/microservice.config';

async function bootstrap() {
  // 创建 HTTP 应用
  const app = await NestFactory.create(AppModule);

  // 注册微服务
  app.connectMicroservice<MicroserviceOptions>(microserviceConfig.rabbitmq);
  app.connectMicroservice<MicroserviceOptions>(microserviceConfig.grpc);

  // 启动所有微服务
  await app.startAllMicroservices();
  // 启动 HTTP 服务
  await app.listen(3000);
}
bootstrap();

消息队列集成

1. RabbitMQ 集成

typescript 复制代码
// src/modules/orders/orders.module.ts
import { Module } from '@nestjs/common';
import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
import { OrdersController } from './orders.controller';
import { OrdersService } from './orders.service';

@Module({
  imports: [
    RabbitMQModule.forRoot(RabbitMQModule, {
      exchanges: [
        {
          name: 'orders',
          type: 'topic',
        },
      ],
      uri: process.env.RABBITMQ_URL,
      connectionInitOptions: { wait: false },
    }),
  ],
  controllers: [OrdersController],
  providers: [OrdersService],
})
export class OrdersModule {}

// src/modules/orders/orders.service.ts
import { Injectable } from '@nestjs/common';
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { CreateOrderDto } from './dto/create-order.dto';

@Injectable()
export class OrdersService {
  constructor(private readonly amqpConnection: AmqpConnection) {}

  async createOrder(createOrderDto: CreateOrderDto) {
    // 发布订单创建事件
    await this.amqpConnection.publish(
      'orders',
      'order.created',
      {
        orderId: createOrderDto.id,
        userId: createOrderDto.userId,
        amount: createOrderDto.amount,
        timestamp: new Date(),
      },
    );

    return { message: 'Order created successfully' };
  }

  // 消费订单事件
  @RabbitSubscribe({
    exchange: 'orders',
    routingKey: 'order.*',
    queue: 'order-processing-queue',
  })
  async handleOrderEvent(msg: any) {
    console.log('Received order event:', msg);
    // 处理订单事件
    await this.processOrder(msg);
  }
}

2. Kafka 集成

typescript 复制代码
// src/modules/events/events.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { EventsController } from './events.controller';
import { EventsService } from './events.service';

@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'KAFKA_SERVICE',
        transport: Transport.KAFKA,
        options: {
          client: {
            clientId: 'events',
            brokers: ['localhost:9092'],
          },
          consumer: {
            groupId: 'events-consumer',
          },
        },
      },
    ]),
  ],
  controllers: [EventsController],
  providers: [EventsService],
})
export class EventsModule {}

// src/modules/events/events.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { ClientKafka } from '@nestjs/microservices';

@Injectable()
export class EventsService {
  constructor(
    @Inject('KAFKA_SERVICE')
    private readonly kafkaClient: ClientKafka,
  ) {}

  async publishEvent(event: any) {
    return this.kafkaClient.emit('events', event);
  }

  @MessagePattern('events')
  async handleEvent(event: any) {
    console.log('Received event:', event);
    // 处理事件
    await this.processEvent(event);
  }
}

gRPC 服务实现

1. Proto 文件定义

protobuf 复制代码
// src/proto/hero.proto
syntax = "proto3";

package hero;

service HeroService {
  rpc FindOne (HeroById) returns (Hero) {}
  rpc FindAll (Empty) returns (Heroes) {}
}

message HeroById {
  int32 id = 1;
}

message Hero {
  int32 id = 1;
  string name = 2;
  string power = 3;
}

message Heroes {
  repeated Hero heroes = 1;
}

message Empty {}

2. gRPC 服务实现

typescript 复制代码
// src/modules/heroes/heroes.controller.ts
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { HeroesService } from './heroes.service';

@Controller()
export class HeroesController {
  constructor(private readonly heroesService: HeroesService) {}

  @GrpcMethod('HeroService', 'FindOne')
  async findOne(data: { id: number }) {
    return this.heroesService.findOne(data.id);
  }

  @GrpcMethod('HeroService', 'FindAll')
  async findAll() {
    return { heroes: await this.heroesService.findAll() };
  }
}

// src/modules/heroes/heroes.service.ts
import { Injectable } from '@nestjs/common';
import { Hero } from './interfaces/hero.interface';

@Injectable()
export class HeroesService {
  private readonly heroes: Hero[] = [
    { id: 1, name: 'John', power: 'Time Control' },
    { id: 2, name: 'Alice', power: 'Telekinesis' },
  ];

  async findOne(id: number): Promise<Hero> {
    return this.heroes.find(hero => hero.id === id);
  }

  async findAll(): Promise<Hero[]> {
    return this.heroes;
  }
}

服务发现注册

1. Consul 集成

typescript 复制代码
// src/modules/discovery/consul.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import * as Consul from 'consul';

@Injectable()
export class ConsulService implements OnModuleInit {
  private readonly consul: Consul.Consul;

  constructor() {
    this.consul = new Consul({
      host: process.env.CONSUL_HOST || 'localhost',
      port: process.env.CONSUL_PORT || '8500',
    });
  }

  async onModuleInit() {
    // 注册服务
    await this.registerService();
    // 健康检查
    await this.registerHealthCheck();
  }

  private async registerService() {
    await this.consul.agent.service.register({
      name: process.env.SERVICE_NAME,
      id: process.env.SERVICE_ID,
      address: process.env.SERVICE_HOST,
      port: parseInt(process.env.SERVICE_PORT),
      tags: ['nestjs', 'microservice'],
      check: {
        http: `http://${process.env.SERVICE_HOST}:${process.env.SERVICE_PORT}/health`,
        interval: '10s',
      },
    });
  }

  async discoverService(serviceName: string): Promise<string> {
    const services = await this.consul.catalog.service.nodes(serviceName);
    if (services.length === 0) {
      throw new Error(`Service ${serviceName} not found`);
    }
    // 简单的负载均衡:随机选择一个服务实例
    const service = services[Math.floor(Math.random() * services.length)];
    return `http://${service.ServiceAddress}:${service.ServicePort}`;
  }
}

2. 服务健康检查

typescript 复制代码
// src/modules/health/health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheck, HealthCheckService } from '@nestjs/terminus';
import { TypeOrmHealthIndicator } from '@nestjs/terminus';

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private db: TypeOrmHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      // 数据库健康检查
      () => this.db.pingCheck('database'),
      // 自定义健康检查
      () => this.checkServiceHealth(),
    ]);
  }

  private async checkServiceHealth() {
    // 实现自定义健康检查逻辑
    return {
      service: {
        status: 'up',
        details: {
          version: process.env.SERVICE_VERSION,
          uptime: process.uptime(),
        },
      },
    };
  }
}

分布式配置

1. 配置中心集成

typescript 复制代码
// src/config/config.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV}`,
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production', 'test')
          .default('development'),
        PORT: Joi.number().default(3000),
        DATABASE_URL: Joi.string().required(),
        RABBITMQ_URL: Joi.string().required(),
        KAFKA_BROKERS: Joi.string().required(),
        CONSUL_HOST: Joi.string().required(),
        CONSUL_PORT: Joi.number().required(),
      }),
    }),
  ],
  providers: [ConfigService],
  exports: [ConfigService],
})
export class AppConfigModule {}

2. 动态配置更新

typescript 复制代码
// src/config/dynamic-config.service.ts
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConsulService } from '../discovery/consul.service';
import { Subject } from 'rxjs';

@Injectable()
export class DynamicConfigService implements OnModuleInit {
  private configSubject = new Subject<any>();
  private config: any = {};

  constructor(private readonly consulService: ConsulService) {}

  async onModuleInit() {
    // 初始化配置
    await this.loadConfig();
    // 监听配置变更
    this.watchConfigChanges();
  }

  private async loadConfig() {
    const configKeys = ['database', 'rabbitmq', 'kafka'];
    for (const key of configKeys) {
      const value = await this.consulService.kv.get(`config/${key}`);
      this.config[key] = JSON.parse(value);
    }
    this.configSubject.next(this.config);
  }

  private watchConfigChanges() {
    this.consulService.watch({
      method: this.consulService.kv.get,
      options: { key: 'config/', recurse: true },
    }).on('change', async () => {
      await this.loadConfig();
    });
  }

  getConfig<T>(key: string): T {
    return this.config[key];
  }

  onConfigUpdate() {
    return this.configSubject.asObservable();
  }
}

服务间通信

1. HTTP 客户端

typescript 复制代码
// src/modules/http/http.service.ts
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConsulService } from '../discovery/consul.service';
import { firstValueFrom } from 'rxjs';

@Injectable()
export class ApiService {
  constructor(
    private readonly httpService: HttpService,
    private readonly consulService: ConsulService,
  ) {}

  async callService(serviceName: string, path: string, method = 'GET', data?: any) {
    // 服务发现
    const serviceUrl = await this.consulService.discoverService(serviceName);

    // 构建请求
    const request = {
      method,
      url: `${serviceUrl}${path}`,
      ...(data && { data }),
    };

    try {
      const response = await firstValueFrom(this.httpService.request(request));
      return response.data;
    } catch (error) {
      // 处理错误,可能需要重试或熔断
      this.handleError(error);
    }
  }

  private handleError(error: any) {
    // 实现错误处理逻辑
    if (error.response) {
      // 服务端错误
      throw new Error(`Service error: ${error.response.status}`);
    } else if (error.request) {
      // 网络错误
      throw new Error('Network error');
    } else {
      // 其他错误
      throw error;
    }
  }
}

2. 事件总线

typescript 复制代码
// src/modules/events/event-bus.service.ts
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';

@Injectable()
export class EventBusService {
  constructor(private eventEmitter: EventEmitter2) {}

  async publish(event: string, payload: any) {
    this.eventEmitter.emit(event, payload);
  }

  async subscribe(event: string, callback: (payload: any) => void) {
    this.eventEmitter.on(event, callback);
  }
}

// 使用示例
@Injectable()
export class OrderService {
  constructor(private eventBus: EventBusService) {}

  async createOrder(order: any) {
    // 创建订单
    const newOrder = await this.orderRepository.save(order);

    // 发布事件
    await this.eventBus.publish('order.created', newOrder);

    return newOrder;
  }
}

@Injectable()
export class NotificationService {
  constructor(private eventBus: EventBusService) {
    // 订阅事件
    this.eventBus.subscribe('order.created', this.handleOrderCreated.bind(this));
  }

  private async handleOrderCreated(order: any) {
    // 处理订单创建事件
    await this.sendNotification(order);
  }
}

写在最后

本文详细介绍了 NestJS 中的微服务架构实现:

  1. 微服务基础配置
  2. 消息队列集成
  3. gRPC 服务实现
  4. 服务发现与注册
  5. 分布式配置管理
  6. 服务间通信方案

在下一篇文章中,我们将探讨 NestJS 的性能优化策略。

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

相关推荐
摘星编程9 分钟前
CANN内存管理机制:从分配策略到性能优化
人工智能·华为·性能优化
likerhood16 分钟前
3. pytorch中数据集加载和处理
人工智能·pytorch·python
Robot侠17 分钟前
ROS1从入门到精通 10:URDF机器人建模(从零构建机器人模型)
人工智能·机器人·ros·机器人操作系统·urdf机器人建模
haiyu_y18 分钟前
Day 46 TensorBoard 使用介绍
人工智能·深度学习·神经网络
阿里云大数据AI技术23 分钟前
DataWorks 又又又升级了,这次我们通过 Arrow 列存格式让数据同步速度提升10倍!
大数据·人工智能
做科研的周师兄24 分钟前
中国土壤有机质数据集
人工智能·算法·机器学习·分类·数据挖掘
IT一氪25 分钟前
一款 AI 驱动的 Word 文档翻译工具
人工智能·word
lovingsoft28 分钟前
Vibe coding 氛围编程
人工智能
百***074533 分钟前
GPT-Image-1.5 极速接入全流程及关键要点
人工智能·gpt·计算机视觉
羽沢311 小时前
ECharts 学习
前端·学习·echarts