文章主要介绍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日志服务