nestjs集成grpc服务 - 指南

h5打开以查看

一、基础配置方法

1. 项目结构准备

text

src/
├── grpc/
│   ├── user/
│   │   └── user.proto
│   ├── order/
│   │   └── order.proto
│   └── product/
│       └── product.proto
├── user/
│   ├── user.module.ts
│   └── user.service.ts
├── order/
│   ├── order.module.ts
│   └── order.service.ts
└── product/
    ├── product.module.ts
    └── product.service.ts

2. 定义多个 Proto 文件

user.proto

protobuf

syntax = "proto3";

package user;

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse) {}
  rpc CreateUser (CreateUserRequest) returns (UserResponse) {}
}

message UserRequest {
  string id = 1;
}

message CreateUserRequest {
  string name = 1;
  string email = 2;
}

message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

order.proto

protobuf

syntax = "proto3";

package order;

service OrderService {
  rpc GetOrder (OrderRequest) returns (OrderResponse) {}
  rpc CreateOrder (CreateOrderRequest) returns (OrderResponse) {}
}

message OrderRequest {
  string id = 1;
}

message CreateOrderRequest {
  string userId = 1;
  repeated string productIds = 2;
}

message OrderResponse {
  string id = 1;
  string userId = 2;
  repeated string productIds = 3;
  string status = 4;
}

二、方式一:使用多个微服务客户端(推荐)

1. 配置多个 gRPC 客户端

app.module.ts

typescript

import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { UserModule } from './user/user.module';
import { OrderModule } from './order/order.module';

@Module({
  imports: [
    ClientsModule.register([
      // 用户服务 gRPC 客户端
      {
        name: 'USER_PACKAGE',
        transport: Transport.GRPC,
        options: {
          package: 'user',
          protoPath: join(__dirname, 'grpc/user/user.proto'),
          url: 'localhost:50051', // 用户服务地址
        },
      },
      // 订单服务 gRPC 客户端
      {
        name: 'ORDER_PACKAGE',
        transport: Transport.GRPC,
        options: {
          package: 'order',
          protoPath: join(__dirname, 'grpc/order/order.proto'),
          url: 'localhost:50052', // 订单服务地址
        },
      },
    ]),
    UserModule,
    OrderModule,
  ],
})
export class AppModule {}

2. 创建用户服务

user/user.service.ts

typescript

import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { Observable } from 'rxjs';

interface UserServiceClient {
  getUser(data: { id: string }): Observable;
  createUser(data: { name: string; email: string }): Observable;
}

@Injectable()
export class UserService implements OnModuleInit {
  private userService: UserServiceClient;

  constructor(@Inject('USER_PACKAGE') private client: ClientGrpc) {}

  onModuleInit() {
    this.userService = this.client.getService('UserService');
  }

  async getUser(id: string) {
    return this.userService.getUser({ id });
  }

  async createUser(name: string, email: string) {
    return this.userService.createUser({ name, email });
  }
}

user/user.module.ts

typescript

import { Module } from '@nestjs/common';
import { UserService } from './user.service';

@Module({
  providers: [UserService],
  exports: [UserService],
})
export class UserModule {}

3. 创建订单服务

order/order.service.ts

typescript

import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { Observable } from 'rxjs';

interface OrderServiceClient {
  getOrder(data: { id: string }): Observable;
  createOrder(data: { userId: string; productIds: string[] }): Observable;
}

@Injectable()
export class OrderService implements OnModuleInit {
  private orderService: OrderServiceClient;

  constructor(@Inject('ORDER_PACKAGE') private client: ClientGrpc) {}

  onModuleInit() {
    this.orderService = this.client.getService('OrderService');
  }

  async getOrder(id: string) {
    return this.orderService.getOrder({ id });
  }

  async createOrder(userId: string, productIds: string[]) {
    return this.orderService.createOrder({ userId, productIds });
  }
}

order/order.module.ts

typescript

import { Module } from '@nestjs/common';
import { OrderService } from './order.service';

@Module({
  providers: [OrderService],
  exports: [OrderService],
})
export class OrderModule {}

4. 在控制器中使用多个服务

app.controller.ts

typescript

import { Controller, Get, Post, Body, Inject } from '@nestjs/common';
import { UserService } from './user/user.service';
import { OrderService } from './order/order.service';

@Controller()
export class AppController {
  constructor(
    private readonly userService: UserService,
    private readonly orderService: OrderService,
  ) {}

  @Get('user/:id')
  async getUser(@Param('id') id: string) {
    return this.userService.getUser(id);
  }

  @Post('order')
  async createOrder(@Body() createOrderDto: { userId: string; productIds: string[] }) {
    // 先验证用户是否存在
    const user = await this.userService.getUser(createOrderDto.userId);
    
    // 然后创建订单
    const order = await this.orderService.createOrder(
      createOrderDto.userId,
      createOrderDto.productIds,
    );
    
    return {
      user: user,
      order: order,
    };
  }
}

三、方式二:动态配置多个 gRPC 服务

1. 创建配置工厂

grpc/grpc.config.ts

typescript

import { Transport } from '@nestjs/microservices';
import { join } from 'path';

export const grpcClientOptions = {
  user: {
    transport: Transport.GRPC,
    options: {
      package: 'user',
      protoPath: join(__dirname, 'user/user.proto'),
      url: 'localhost:50051',
    },
  },
  order: {
    transport: Transport.GRPC,
    options: {
      package: 'order',
      protoPath: join(__dirname, 'order/order.proto'),
      url: 'localhost:50052',
    },
  },
  product: {
    transport: Transport.GRPC,
    options: {
      package: 'product',
      protoPath: join(__dirname, 'product/product.proto'),
      url: 'localhost:50053',
    },
  },
};

2. 动态模块配置

grpc/grpc.module.ts

typescript

import { DynamicModule, Module } from '@nestjs/common';
import { ClientsModule } from '@nestjs/microservices';
import { grpcClientOptions } from './grpc.config';

@Module({})
export class GrpcModule {
  static register(clients: string[]): DynamicModule {
    const clientConfigs = clients.map(clientName => ({
      name: clientName.toUpperCase() + '_PACKAGE',
      ...grpcClientOptions[clientName],
    }));

    return {
      module: GrpcModule,
      imports: [ClientsModule.register(clientConfigs)],
      exports: [ClientsModule],
    };
  }
}

3. 使用动态模块

app.module.ts

typescript

import { Module } from '@nestjs/common';
import { GrpcModule } from './grpc/grpc.module';
import { UserService } from './user/user.service';
import { OrderService } from './order/order.service';

@Module({
  imports: [
    GrpcModule.register(['user', 'order', 'product']),
    // ... 其他模块
  ],
  providers: [UserService, OrderService],
})
export class AppModule {}

四、方式三:使用配置文件和异步注册

1. 配置文件

config/grpc.config.ts

typescript

export default () => ({
  grpc: {
    services: {
      user: {
        package: 'user',
        protoPath: 'grpc/user/user.proto',
        url: process.env.USER_GRPC_URL || 'localhost:50051',
      },
      order: {
        package: 'order',
        protoPath: 'grpc/order/order.proto',
        url: process.env.ORDER_GRPC_URL || 'localhost:50052',
      },
      product: {
        package: 'product',
        protoPath: 'grpc/product/product.proto',
        url: process.env.PRODUCT_GRPC_URL || 'localhost:50053',
      },
    },
  },
});

2. 异步配置模块

grpc/grpc-client.module.ts

typescript

import { DynamicModule, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { join } from 'path';

@Module({})
export class GrpcClientModule {
  static forRoot(serviceNames: string[]): DynamicModule {
    const clients = serviceNames.map(serviceName => ({
      name: `${serviceName.toUpperCase()}_PACKAGE`,
      useFactory: (configService: ConfigService) => {
        const serviceConfig = configService.get(`grpc.services.${serviceName}`);
        return {
          transport: Transport.GRPC,
          options: {
            package: serviceConfig.package,
            protoPath: join(__dirname, '..', serviceConfig.protoPath),
            url: serviceConfig.url,
          },
        };
      },
      inject: [ConfigService],
    }));

    return {
      module: GrpcClientModule,
      imports: [
        ClientsModule.registerAsync(clients),
      ],
      exports: [ClientsModule],
    };
  }
}

3. 在主模块中使用

app.module.ts

typescript

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { GrpcClientModule } from './grpc/grpc-client.module';
import configuration from './config/configuration';

@Module({
  imports: [
    ConfigModule.forRoot({
      load: [configuration],
    }),
    GrpcClientModule.forRoot(['user', 'order', 'product']),
    // ... 其他模块
  ],
})
export class AppModule {}

五、统一服务工厂模式

创建统一的 gRPC 服务工厂

grpc/grpc-factory.service.ts

typescript

import { Injectable, Inject, OnModuleInit } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { Observable } from 'rxjs';

@Injectable()
export class GrpcServiceFactory implements OnModuleInit {
  private services = new Map();

  constructor(
    @Inject('USER_PACKAGE') private userClient: ClientGrpc,
    @Inject('ORDER_PACKAGE') private orderClient: ClientGrpc,
    @Inject('PRODUCT_PACKAGE') private productClient: ClientGrpc,
  ) {}

  onModuleInit() {
    // 初始化所有服务
    this.services.set('user', this.userClient.getService('UserService'));
    this.services.set('order', this.orderClient.getService('OrderService'));
    this.services.set('product', this.productClient.getService('ProductService'));
  }

  getService(serviceName: string) {
    const service = this.services.get(serviceName);
    if (!service) {
      throw new Error(`gRPC service ${serviceName} not found`);
    }
    return service;
  }

  // 快捷方法
  getUserService() {
    return this.getService('user');
  }

  getOrderService() {
    return this.getService('order');
  }

  getProductService() {
    return this.getService('product');
  }
}

六、最佳实践建议

  1. 服务发现集成:在生产环境中,建议使用服务发现(如 Consul、Nacos)而不是硬编码 URL

  2. 连接管理:配置连接池和重试策略

  3. 错误处理:统一处理 gRPC 错误

  4. 超时设置:为每个服务设置合理的超时时间

  5. 健康检查:实现 gRPC 健康检查

typescript

// 示例:带超时和重试的配置
{
  transport: Transport.GRPC,
  options: {
    package: 'user',
    protoPath: join(__dirname, 'grpc/user/user.proto'),
    url: 'localhost:50051',
    maxRetries: 3,
    timeout: 5000, // 5秒超时
    keepalive: {
      keepaliveTimeMs: 30000,
      keepaliveTimeoutMs: 10000,
    },
  },
}

通过以上方式,可以在 NestJS 中灵活地调用多个 gRPC 服务的接口,并根据项目需求选择合适的配置方案。

h5打开以查看

posted @ 2025-11-30 13:18  yangykaifa  阅读(0)  评论(0)    收藏  举报