by 雪隐 from https://juejin.cn/user/1433418895994094
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可联系授权
在Nest.js中,我们有一些为微服务准备的传输层实现。其中之一是gRPC转运体,这无疑是最有趣的转运体之一。在本文中,我们将探讨这一层背后的想法,以及如何在NestJS中实现它。
在弄脏你的手并在NestJs中创建我们的微服务之前,如果你想了解更多关于微服务的信息,请访问下面的帖子:
-
NestJS小技巧10-使用Nest.JS在Node中实现可扩展的微服务— juejin.cn/post/721912…
gRPC
gRPC是由Google开发的一种高性能的远程过程调用(RPC)框架,它可以使客户端应用程序和服务器应用程序通过网络进行通信,以快速高效地交换数据和请求远程服务。
gRPC使用Protocol Buffers作为默认的序列化机制,它是一种语言无关的二进制格式,用于编码结构化数据。它支持多种编程语言,包括C ++,Java,Python和Go,并提供一元和流式RPC。
gRPC旨在轻量级、快速和可靠。它基于HTTP/2标准构建,可以提供高效的流控制、多路复用和头部压缩,从而在减少网络延迟的同时提高了吞吐量。此外,gRPC还支持各种身份验证和授权机制,例如基于TLS的身份验证、OAuth2和JWT。
通过使用gRPC,开发人员可以快速构建跨平台和跨语言的应用程序,同时获得高性能、可靠性和安全性。
使用gRPC有以下几个优点:
- 高性能:gRPC使用基于HTTP/2的传输协议,可以实现高效的流控制、多路复用和头部压缩,从而减少了网络延迟和带宽占用,提高了吞吐量。
- 省时省力:gRPC使用Protocol Buffers作为默认的序列化机制,可以将结构化数据转换为二进制格式,从而减少了数据传输的大小和时间,提高了效率。同时,gRPC提供了代码自动生成工具,可以自动生成客户端和服务端的代码,省去了手动编写代码的过程。
- 跨平台和跨语言:gRPC支持多种编程语言和平台,例如C++, Java, Python和Go等,使得开发人员可以在不同的语言和平台之间轻松地通信和交换数据。
- 可靠性:gRPC支持各种身份验证和授权机制,例如基于TLS的身份验证、OAuth2和JWT等,从而提高了系统的安全性和可靠性。
总之,使用gRPC可以帮助开发人员快速构建高性能、跨平台和可靠的分布式系统,提高开发效率和用户体验。
创建项目模型
详细内容参照官方文档,跟着官方的说明,我们来跟着做一下
1. 客户端
客户端需要说明的东西不多,有关的就是注册模型,其他的都和一般的NestJS一样。
- 安装必要的软件包
$ pnpm i @nestjs/microservices @grpc/proto-loader @grpc/grpc-js
- 使用Nestjs CLI创建了一个项目,并定义了一个名为hero的模块。
$ nest new client
$ nest g resource hero --no-spec
# 选RestAPI, 不要CURD
- 现在让我们创建.proto文件,它的工作是描述我们想要远程使用的服务和方法。
syntax = "proto3";
package hero;
service HeroService {
rpc FindOne (HeroById) returns (Hero);
rpc FindMany (stream HeroById) returns (stream Hero);
}
message HeroById {
int32 id = 1;
}
message Hero {
int32 id = 1;
string name = 2;
}
- 在hero模块中注册gRPC服务。
@Module({
imports: [
ClientsModule.register([
{
name: 'HERO_PACKAGE',
...grpcClientOptions,
},
]),
],
controllers: [HeroController],
providers: [HeroService]
})
export class HeroModule {}
gRPC配置信息
import { ClientOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
export const grpcClientOptions: ClientOptions = {
transport: Transport.GRPC,
options: {
package: 'hero', // ['hero', 'hero2']
protoPath: join(__dirname, './hero/hero.proto'), // ['./hero/hero.proto', './hero/hero2.proto']
url: '0.0.0.0:5000'
},
};
gRPC配置说明
| 参数 | 说明 |
|---|---|
package | Protobuf包名称(与.proto文件中的包设置匹配)。必须 |
protoPath | “.proto”文件的绝对(或相对于根目录)路径。必需 |
url | 连接url。格式为ip address/dns name:port(例如,“localhost:50051”)的字符串,用于定义传输程序建立连接的地址/端口。可选择的默认为“localhost:5000” |
protoLoader | 用于加载.proto文件的实用程序的NPM包名称。可选。默认为“@grpc/proto-loader” |
loader | @grpc/proto-loader选项。它们提供了对“.proto”文件行为的详细控制。可选。请参见此处了解更多详细信息 |
credentials | S服务器凭据。可选。点击此处阅读更多信息 |
- 在控制器中通过依赖注入引入gRPC服务
import { Controller, Get, Inject, OnModuleInit, Param } from '@nestjs/common';
import {
ClientGrpc,
} from '@nestjs/microservices';
import { Observable, ReplaySubject } from 'rxjs';
import { toArray } from 'rxjs/operators';
import { HeroById } from './interfaces/hero-by-id.interface';
import { Hero } from './interfaces/hero.interface';
interface HeroService {
findOne(data: HeroById): Observable<Hero>;
findMany(upstream: Observable<HeroById>): Observable<Hero>;
}
@Controller('hero')
export class HeroController implements OnModuleInit {
private readonly items: Hero[] = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
];
private heroService: HeroService;
constructor(@Inject('HERO_PACKAGE') private readonly client: ClientGrpc) {}
onModuleInit() {
this.heroService = this.client.getService<HeroService>('HeroService');
}
@Get()
getMany(): Observable<Hero[]> {
const ids$ = new ReplaySubject<HeroById>();
ids$.next({ id: 1 });
ids$.next({ id: 2 });
ids$.complete();
const stream = this.heroService.findMany(ids$.asObservable());
return stream.pipe(toArray());
}
@Get(':id')
getById(@Param('id') id: string): Observable<Hero> {
return this.heroService.findOne({ id: +id });
}
}
2. gRPC服务
- 安装必要的软件包
$ pnpm i @nestjs/microservices @grpc/proto-loader @grpc/grpc-js
- 使用Nestjs CLI创建了一个项目,并定义了一个名为hero的模块。
$ nest new microservice1
$ nest g resource hero --no-spec
# 选RestAPI, 不要CURD
- 让我们更改文件
main.ts,创建一个微服务。grpcClientOptions和客户端的一样。
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions } from '@nestjs/microservices';
import { AppModule } from './app.module';
import { grpcClientOptions } from './grpc-client.options';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
...grpcClientOptions
});
await app.listen();
}
bootstrap();
- 写gRPC服务,proto和客户端的一样。
一般的gRPC方法用装饰器@GrpcMethod装饰就可以使用了,具体传参的内容并不复杂,大家看官网说明就行了。
gRPC本身支持长期实时连接,通常称为流。流对于聊天、订阅或大量数据传输等情况很有用。点击此处查看官方文档中的更多详细信息。
Nest以两种可能的方式支持GRPC流处理程序:
- RxJS Subject+Observable处理程序:可以在
Controller方法内部编写响应,也可以传递给Subject/Observable使用者 - 纯GRPC调用流处理程序:传递给某个执行器可能很有用,该执行器将处理节点标准“双工”流处理程序的其余调度。
gRPC的代码如下:
import { Controller } from '@nestjs/common';
import {
GrpcMethod,
GrpcStreamMethod,
} from '@nestjs/microservices';
import { Observable, Subject } from 'rxjs';
import { HeroById } from './interfaces/hero-by-id.interface';
import { Hero } from './interfaces/hero.interface';
@Controller('hero')
export class HeroController {
private readonly items: Hero[] = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Doe' },
];
@GrpcMethod('HeroService')
findOne(data: HeroById): Hero {
return this.items.find(({ id }) => id === data.id);
}
@GrpcStreamMethod('HeroService')
findMany(data$: Observable<HeroById>): Observable<Hero> {
const hero$ = new Subject<Hero>();
const onNext = (heroById: HeroById) => {
const item = this.items.find(({ id }) => id === heroById.id);
hero$.next(item);
};
const onComplete = () => hero$.complete();
data$.subscribe({
next: onNext,
complete: onComplete,
});
return hero$.asObservable();
}
}
在客户端怎么调用流,请参照官方文档
3. 运行看效果
同时启动 微服务和客户端服务。并访问http://localhost:3000/hero/1,返回的结果如下。
{"id":1,"name":"John"}
结论
欢迎使用Nest.js开发gRPC微服务。如果觉得这篇文章对您有帮助,请点赞/评论。谢谢大家!
本章代码
client 和 microservice1 是一组,micro-and-client是把服务和grpc服务放在一起写了。