【Nest】初探Nest+微服务+gRPC

文章主要介绍Nest微服务是什么?如何运行?以及搭配gRPC使用栗子。

微服务简介

微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的 API 进行通信的小型独立服务组成。

特性

  • 自主性

可以对微服务架构中的每个组件服务进行开发、部署、运营和扩展,而不影响其他服务的功能。这些服务不需要与其他服务共享任何代码或实施。各个组件之间的任何通信都是通过明确定义的 API 进行的。

  • 专用性

每项服务都是针对一组功能而设计的,并专注于解决特定的问题。如果开发人员逐渐将更多代码增加到一项服务中并且这项服务变得复杂,那么可以将其拆分成多项更小的服务。

优势

  • 敏捷
  • 灵活拓展
  • 轻松部署
  • 技术自由
  • 可重复使用代码
  • 弹性

Nest微服务介绍

Nest 支持几种内置的传输层实现,称为传输器,负责在不同的微服务实例之间传输消息。大多数传输器本机都支持请求 - 响应和基于事件的消息样式。Nest 在规范接口的后面抽象了每个传输器的实现细节,用于请求 - 响应和基于事件的消息传递。这样可以轻松地从一个传输层切换到另一层,例如,利用特定传输层的特定可靠性或性能功能,而不会影响您的应用程序代码。

项目结构

新建Nest项目

shell 复制代码
nest new micro-main // 新建grpc客户端
nest new micro-user // 新建用户模块项目

安装依赖

shell 复制代码
npm i --save @nestjs/microservices 
npm i --save @grpc/grpc-js @grpc/proto-loader

项目搭建

新建user.proto文件使用protoc生成proto.pb.ts文件

proto文件编写

ini 复制代码
syntax = "proto3";

package user;

service UserService {
  rpc FindOne (FindOneReq) returns (FindOneRes) {}
}

message User {
  int64 id = 1;
  string name = 2;
  string password = 3;
  string avatar = 4;
}

message FindOneReq {
  int64 id = 1;
}
message FindOneRes {
  User user = 1;
}

使用protoc生成.pb文件

typescript 复制代码
/* eslint-disable */
import { GrpcMethod, GrpcStreamMethod } from "@nestjs/microservices";
import { Observable } from "rxjs";

export const protobufPackage = "user";

export interface User {
  id: number;
  name: string;
  password: string;
  avatar: string;
}

export interface FindOneReq {
  id: number;
}

export interface FindOneRes {
  user: User | undefined;
}

export const USER_PACKAGE_NAME = "user";

export interface UserServiceClient {
  findOne(request: FindOneReq): Observable<FindOneRes>;
}

export interface UserServiceController {
  findOne(request: FindOneReq): Promise<FindOneRes> | Observable<FindOneRes> | FindOneRes;

}

export function UserServiceControllerMethods() {
  return function (constructor: Function) {
    const grpcMethods: string[] = ["findOne"];
    for (const method of grpcMethods) {
      const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
      GrpcMethod("UserService", method)(constructor.prototype[method], method, descriptor);
    }
    const grpcStreamMethods: string[] = [];
    for (const method of grpcStreamMethods) {
      const descriptor: any = Reflect.getOwnPropertyDescriptor(constructor.prototype, method);
      GrpcStreamMethod("UserService", method)(constructor.prototype[method], method, descriptor);
    }
  };
}

export const USER_SERVICE_NAME = "UserService";

user模块搭建

改造main

ts 复制代码
import { NestFactory } from '@nestjs/core';
import { AppModule } from './user.module';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { USER_PACKAGE_NAME } from '@pb/user.pb';
async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.GRPC,
      options: {
        url: '0.0.0.0:50051',
        protoPath: './../micro-proto/user.proto',
        package: USER_PACKAGE_NAME,
      },
    },
  );
  await app.listen();
}
bootstrap();

编写user.controller

ts 复制代码
import { Controller } from '@nestjs/common';
import { UserService } from './user.service';
import { GrpcMethod } from '@nestjs/microservices';
import { FindOneReq, USER_SERVICE_NAME} from '@pb/user.pb';
import { Metadata, ServerUnaryCall } from '@grpc/grpc-js';

@Controller('user')
export class UserController {
  constructor() {}

  @GrpcMethod(USER_SERVICE_NAME, 'FindOne')
  FindOne(
    data: FindOneReq,
    metadata: Metadata,
    call: ServerUnaryCall<any, any>,
  ) {
    return {
        id: 1,
        name: 'test',
        password: 'string',
        avatar: 'string',
      };
  }
}

微服务客户端搭建

新增user文件夹并创建user.controller以及user.module

user.controller

ts 复制代码
import { Controller, Get, Inject, OnModuleInit, Post } from '@nestjs/common';
import { ClientGrpc, ClientProxy } from '@nestjs/microservices';
import { Observable } from 'rxjs';
import { Metadata } from '@grpc/grpc-js';
import {
  FindOneReq,
  FindOneRes,
  USER_PACKAGE_NAME,
  USER_SERVICE_NAME,
  UserServiceClient,
  UserServiceController,
} from '@pb/user.pb';
@Controller(USER_PACKAGE_NAME)
export class UserController implements OnModuleInit {
  private userService: UserServiceController;
  constructor(
    @Inject(USER_SERVICE_NAME) private readonly client: ClientGrpc) {}
  onModuleInit() {
      this.userService = this.client.getService<UserServiceClient>(USER_SERVICE_NAME);
  }

  @Post('findOne')
  findOne() {
    return this.userService.findOne(1); // 内容为演示demo
  }
}

user.module

ts 复制代码
import { Module } from '@nestjs/common';
import { ClientsModule, Transport } from '@nestjs/microservices';
import { UserController } from './user.controller';
import { resolve } from 'path';
import { USER_PACKAGE_NAME, USER_SERVICE_NAME } from '@pb/user.pb';
@Module({
  imports: [
    ClientsModule.register([
      {
        name: USER_SERVICE_NAME,
        transport: Transport.GRPC,
        options: {
          url: '0.0.0.0:50051',
          protoPath: resolve(
            __dirname,
            '../micro-proto/user.proto',
          ),
          package: USER_PACKAGE_NAME,
        },
      },
    ]),
  ],
  providers: [],
  controllers: [UserController],
})
export class UserModule {}

测试

分别运行主客户端服务以及用户模块在postman中测试POST请http://localhost:3000/user/findOne

得到结果

json 复制代码
{ id: 1, name: 'test', password: 'string', avatar: 'string', }

结尾

初探微服务+grpc跑通服务

感兴趣的同学关注我后续详细补充项目细节以及搭配Redis、kafka以及ELK日志服务

相关推荐
亮子AI17 天前
【NestJS】为什么return不返回客户端?
前端·javascript·git·nestjs
小p18 天前
nestjs学习2:利用typescript改写express服务
nestjs
Eric_见嘉24 天前
NestJS 🧑‍🍳 厨子必修课(九):API 文档 Swagger
前端·后端·nestjs
XiaoYu20021 个月前
第3章 Nest.js拦截器
前端·ai编程·nestjs
XiaoYu20021 个月前
第2章 Nest.js入门
前端·ai编程·nestjs
实习生小黄1 个月前
NestJS 调试方案
后端·nestjs
当时只道寻常1 个月前
NestJS 如何配置环境变量
nestjs
濮水大叔2 个月前
VonaJS是如何做到文件级别精确HMR(热更新)的?
typescript·node.js·nestjs
ovensi2 个月前
告别笨重的 ELK,拥抱轻量级 PLG:NestJS 日志监控实战指南
nestjs