NestJS使用gRPC实现微服务通信

代码仓库地址:github.com/zeng-jc/rpc...

1.1 基本概念

gRPC 基于 Protocol Buffers(protobuf)作为接口定义语言(IDL),意味着你可以使用 protobuf 来定义你的服务接口,gRPC生成的代码可以用于多种语言(C++, Java, Python, Go, C#, Ruby, Node.js),所以使用gRPC就能实现跨语言之间进行微服务通信。

gRPC 基于根据可远程调用的功能(方法)定义服务的概念。 对于每个方法,参数和返回类型需要在.proto文件中定义。

1.2 提供者中proto配置

首先创建创建一个grpc-provider的服务(启动服务说明:npm run start:dev grpc-provider

复制代码
nest generate app grpc-provider

接着上一章我们需要继续安装grpc包

接着上一章我们需要继续安装grpc包

css 复制代码
npm i --save @grpc/grpc-js @grpc/proto-loader

然后在main.ts中做出如下配置

javascript 复制代码
import { NestFactory } from '@nestjs/core';
import { GrpcProviderModule } from './grpc-provider.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
​
async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    GrpcProviderModule,
    {
      // 传输方式GRPC
      transport: Transport.GRPC,
      options: {
        // proto包名
        package: 'hero',
        // proto包的地址
        protoPath: join(__dirname, 'proto/hero.proto'),
        // 你的服务地址
        url: '127.0.0.1:3001',
      },
    },
  );
  await app.listen();
}
bootstrap();

配置完成后此时你还不能立即启动项目,因为nest-cli中还没配置proto文件,所以nestjs并不会对proto文件进行编译。接下来是对nest-cli.json的配置

json 复制代码
{
  "compilerOptions": {
    "assets": ["**/*.proto"],
    "watchAssets": true
  },
}

此时启动项目还是会报错的,因为我们还没有创建proto文件和内容的写入,如下是对proto文件的配置

ini 复制代码
// hero/hero.proto
syntax = "proto3"; //表示proto使用的语法
​
package hero; //proto包名
​
// 定义服务HeroesService
service HeroesService {
  // FindOne方法
    rpc FindOne (HeroById) returns (Hero) {}
}
// FindOne方法参数配置
message HeroById {
  int32 id = 1;
}
// FindOne方法返回值配置
message Hero {
  int32 id = 1;
  string name = 2;
}

1.3 grpc提供者服务实现

我们需要在src目录下先创建heroes资源,没有必要通过nest g res heroes快捷指令创建。创建heroes目录后再创建heroes.controller.tsheroes.module.ts即可,heroes.module.ts记得在grpc-provider.module.ts导入。

typescript 复制代码
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { Metadata, ServerUnaryCall } from '@grpc/grpc-js';
​
interface HeroById {
  id: number;
}
interface Hero {
  id: number;
  name: string;
}
​
@Controller()
export class HeroesController {
  // 如下两个参数都是对应proto文件的内容,两个都可以省略,nestjs会自动转换名字大小写去匹配
  @GrpcMethod('HeroesService', 'FindOne')
  findOne(
    data: HeroById,
    metadata: Metadata,
    call: ServerUnaryCall<any, any>,
  ): Hero {
    console.log('metadata', metadata);
    console.log('call', call);
    const items = [
      { id: 1, name: 'John' },
      { id: 2, name: 'Doe' },
    ];
    return items.find(({ id }) => id === data.id);
  }
}

1.4 grpc客户端服务实现

说明:服务调用者可称为"服务消费者"或"服务客户端"

创建grpc服务客户端:nest generate app grpc-client

grpc-client.module.ts实现,注意在服务客户端也需要一份和提供者相同的proto文件,所以你需要把proto目录接拷贝到当前src目录下,我现在使用的是monorepo模式你也可以创建一个目录来存放所有proto文件。

php 复制代码
import { Module } from '@nestjs/common';
import { GrpcClientController } from './grpc-client.controller';
import { GrpcClientService } from './grpc-client.service';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { join } from 'path';
@Module({
  imports: [
    ClientsModule.register([
      {
        name: 'HERO_SERVICE', //自定义服务名字
        transport: Transport.GRPC,
        options: {
          url: '127.0.0.1:3001', //调用的gRPC服务地址
          package: 'hero',
          protoPath: join(__dirname, '/proto/hero.proto'),
        },
      },
    ]),
  ],
  controllers: [GrpcClientController],
  providers: [GrpcClientService],
})
export class GrpcClientModule {}

grpc-client.controller.ts实现,这里是练习我就直接将代码写入controller中了,实际开发建议写入service中。不知道你是否还记得在前面提供者中也是用到了接口类型HeroById和Hero,在monorepo模式下你也可以对类型进行集中管理,把代码抽出去

typescript 复制代码
import { Controller, Get, Inject, OnModuleInit } from '@nestjs/common';
import { GrpcClientService } from './grpc-client.service';
import { ClientGrpc } from '@nestjs/microservices';
import { Metadata } from '@grpc/grpc-js';
​
interface HeroById {
  id: number;
}
interface Hero {
  id: number;
  name: string;
}
interface HeroesService {
  findOne: (heroById: HeroById, metadata: Metadata) => Hero;
}
​
@Controller()
export class GrpcClientController implements OnModuleInit {
  private heroesService: HeroesService;
  constructor(
    private readonly grpcClientService: GrpcClientService,
    @Inject('HERO_SERVICE') private client: ClientGrpc,
  ) {}
  onModuleInit() {
    this.heroesService = this.client.getService<HeroesService>('HeroesService');
  }
  @Get()
  getHero(): Hero {
    // 第二个参数可以传递元数据
    const metadata = new Metadata();
    metadata.add('Set-Cookie', 'yummy_cookie=choco');
    return this.heroesService.findOne({ id: 1 }, metadata);
  }
}   
相关推荐
晓杰'18 小时前
从0到1实现Balatro游戏后端(5):得分计算与单局结算流程实现
后端·typescript·node.js·游戏开发·项目实战·nestjs·webscoket
追光者♂19 小时前
【测评系列3】CSDN AI数字营销实测体验官:测试 开源项目——Superpowers 游戏引擎从零开发实战指南
人工智能·深度学习·机器学习·typescript·开源·游戏引擎·superpowers
ct97819 小时前
TypeScript 中的泛型
前端·javascript·typescript
烛衔溟1 天前
TypeScript 类的类型 —— 作为类型使用
javascript·ubuntu·typescript
向上的车轮1 天前
NestJS 的优秀替代框架——系统化选型指南(2026视角)
nestjs
阿隅1 天前
TS 深度解析:同为 ? 可选语法,为什么赋值一错一对?类类型与this绑定底层拆解
typescript
Patrick_Wilson2 天前
前端解析接口数据,到底该不该信任后端?聊聊「防御性编程」与「类型契约」的边界
架构·typescript·代码规范
姓蔡小朋友2 天前
TypeScript数据类型
javascript·ubuntu·typescript
烛衔溟2 天前
TypeScript 高级类型与工具类型全解
javascript·ubuntu·typescript
UaoN2 天前
Vibe Coding 时代,为什么 Tailwind + Shadcn/ui 正在成为现代前端的默认答案
react.js·typescript