当前位置: 首页 > news >正文

Nestjs框架: gRPC微服务通信及安全实践全解析

gRPC基础通信实现与Proto转TS方案


原理与配置

gRPC作为现代开源高性能RPC框架,通过Protobuf配置文件生成调用代码,实现跨语言服务通信。
其分布式特性与高性能优势使其成为微服务通信的核心方案。
核心流程分为三个步骤:

1 ) 依赖安装与配置

安装NestJS的gRPC传输器和类型支持 
pnpm add @nestjs/microservices @grpc/grpc-js @grpc/proto-loader 

2 ) Proto文件定义(hero.proto)

文件规范:字段标识符从1开始递增,避免使用19000–19999预留值。
常用类型:int32floatstringboolbytes

syntax = "proto3";package hero;
service HeroService {rpc FindOne (HeroById) returns (Hero) {}
}message HeroById {int32 id = 1;
}message Hero {int32 id = 1;string name = 2;
}

NestJS编译配置(nest-cli.json):

{"compilerOptions": {"assets": ["/*.proto"],"watchAssets": true }
}

3 ) 服务端启动配置(main.ts)

import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';async function bootstrap() {const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {transport: Transport.GRPC,options: {package: 'hero',protoPath: join(__dirname, '../proto/hero.proto'),url: '0.0.0.0:50000'},});await app.listen();
}
bootstrap();

// 示例:NestJS gRPC服务端配置 
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { HeroById, Hero } from './interfaces/hero.interface';@Controller()
export class HeroController {// 模拟@GrpcMethod('HeroService', 'FindOne')findOne(data: HeroById): Hero {const items: Hero[] = [{ id: 1, name: 'John' }, { id: 2, name: 'Doe' }];return items.find(({ id }) => id === data.id);}
}

4 )客户端配置

// 示例:NestJS gRPC客户端配置 
import { Module } from '@nestjs/common';
import { ClientGrpcProxy, ClientModule } from '@nestjs/microservices';
import { join } from 'path';
import { HeroService } from './interfaces/hero.interface';@Module({imports: [ClientModule.register([{name: 'HERO_PACKAGE',transport: Transport.GRPC,options: {package: 'hero',protoPath: join(__dirname, '../proto/hero.proto'),},},]),],providers: [{provide: 'HERO_SERVICE',useFactory: (client: ClientGrpcProxy) => client.getService<HeroService>('HeroService'),inject: ['HERO_PACKAGE'],}],
})
export class AppModule {}

客户端调用

import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { HeroService } from './hero.interface';@Injectable()
export class ClientService {private heroService: HeroService;constructor(@Inject('HERO_PACKAGE') private client: ClientGrpc) {}onModuleInit() {this.heroService = this.client.getService<HeroService>('HeroService');}async getHero(): Promise<Hero> {return this.heroService.findOne({ id: 1 }).toPromise();}
}

5 )类型生成方案对比

方案实现方式适用场景优势
VSCode插件选择.proto文件 → 执行PTS转换快速原型开发即时生成,无需命令行
ts-proto命令行生成类型定义生产环境完整类型和方法支持

方案一:VSCode插件实时转换

  1. 安装vscode-proto3插件
  2. 全选.proto文件内容
  3. 执行Proto > Compile Selection to Typescript
  4. 输出示例:
    export interface Hero {id: number;name: string;
    }
    export interface HeroService {FindOne(request: HeroById): Promise<Hero>;
    }
    

方案二:ts-proto工具链生成

全局安装工具 
npm install -g ts-proto 生成类型定义
protoc --plugin=protoc-gen-ts_proto=./node_modules/.bin/protoc-gen-ts_proto \
--ts_proto_out=./generated \
--ts_proto_opt=outputServices=grpc-js \
proto/hero.proto 
  • 优势:生成完整序列化/反序列化方法,支持复杂类型和流处理
  • 输出内容:包含hero.ts(消息类型)和hero.grpc.ts(服务客户端/服务端桩代码)

安全通信核心代码

核心步骤:通过CA证书链验证服务端与客户端身份,防止中间人攻击

// 服务端SSL配置(NestJS)
import { readFileSync } from 'fs';
import { join } from 'path';
import { ServerCredentials } from '@grpc/grpc-js';const serverCredentials = ServerCredentials.createSsl(readFileSync(join(__dirname, '../certs/ca.pem')), // CA根证书 [{cert_chain: readFileSync(join(__dirname, '../certs/server.pem')), // 服务端证书 private_key: readFileSync(join(__dirname, '../certs/server-key.pem')) // 私钥 }],false // 不强制验证客户端证书 
);// 在main.ts中应用 
const app = await NestFactory.createMicroservice(AppModule, {transport: Transport.GRPC,options: {credentials: serverCredentials,package: 'hero',protoPath: join(__dirname, 'proto/hero.proto'),},
});
// 客户端SSL配置(NestJS)
const clientCredentials = ChannelCredentials.createSsl(readFileSync(join(__dirname, '../certs/ca.pem')) // 仅需CA证书 
);ClientModule.register([{name: 'HERO_PACKAGE',transport: Transport.GRPC,options: {package: 'hero',protoPath: join(__dirname, '../proto/hero.proto'),credentials: clientCredentials, // 注入凭证 url: '0.0.0.0:50000'}
}])

gRPC安全通信进阶实践

证书链问题解决方案
当出现UNAVAILABLE:Failed to get issuer certificate错误时,表明证书链验证失败
根本原因是CA证书未包含完整的信任链:

1 ) 获取根证书

下载缺失的根证书(以ZeroSSL为例)
wget -O certs/root.pem https://secure.trust-provider.com/products/root-certificates/root.crt 合并证书链
cat certs/ca.pem certs/root.pem > certs/full-chain.pem

2 ) 客户端配置修正

// 客户端证书配置(client.module.ts)
import { ChannelCredentials } from '@grpc/grpc-js';const credentials = ChannelCredentials.createSsl(fs.readFileSync(join(__dirname, '../certs/full-chain.pem')),
);@Client({transport: Transport.GRPC,options: {package: 'hero',protoPath: join(__dirname, '../proto/hero.proto'),url: '0.0.0.0:50000',credentials}
})

证书验证机制

组件作用安全级别
根证书信任链顶端,自签名证书最高
中间证书由根证书签发,用于签发终端证书
终端实体证书服务端实际使用的证书标准

同样,如果客户端报错UNABLE_TO_VERIFY_LEAF_SIGNATURE ,根因:CA证书未包含完整证书链(缺失根证书)

解决方案:

  • 通过OpenSSL验证证书链完整性:
    openssl s_client -connect example.com:50100 -CAfile ./certs/ca.pem 
    
  • 补全缺失根证书(如ZeroSSL根证书):
    curl -o ./certs/root_ca.pem https://secure.trust-provider.com/root_ca.pem 
    cat ./certs/root_ca.pem >> ./certs/ca.pem  # 追加到CA文件 
    

gRPC服务测试方案全解析


方案一:grpcurl工具(Go环境)

环境配置

安装Go版本管理工具
bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)安装Go 1.22+版本
gvm install go1.22 -B
gvm use go1.22 --default安装grpcurl 
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest

测试命令示例

非加密测试 
grpcurl -plaintext -proto proto/hero.proto -d '{"id":1}' localhost:50000 hero.HeroService/FindOneSSL加密测试 
grpcurl -cacert certs/full-chain.pem -proto proto/hero.proto -d '{"id":1}' app.grpc.example.com:50000 hero.HeroService/FindOne

方案二:grpc-tools + ts-proto

生成测试客户端

npm install grpc-tools ts-proto 
grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./generated \--grpc_out=grpc_js:./generated \--proto_path=proto proto/hero.proto 

Node.js测试脚本

import * as grpc from '@grpc/grpc-js';
import { HeroServiceClient } from './generated/hero_grpc_pb';
import { HeroById, Hero } from './generated/hero_pb';const client = new HeroServiceClient('localhost:50010',grpc.credentials.createSsl(fs.readFileSync('certs/ca.pem'))
);const request = new HeroById();
request.setId(1);client.findOne(request, (err, response: Hero) => {if (err) console.error(err);else console.log(response.toObject()); // 输出: { id: 1, name: 'John' }
});

方案三:Node.js原生客户端

// test-client.ts
import * as grpc from '@grpc/grpc-js';
import * as protoLoader from '@grpc/proto-loader';
import { join } from 'path';
import fs from 'fs';const PROTO_PATH = join(__dirname, 'proto/hero.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH);
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;const client = new protoDescriptor.hero.HeroService('localhost:50000',grpc.credentials.createSsl(fs.readFileSync(join(__dirname, 'certs/full-chain.pem'))
);client.findOne({ id: 1 }, (err, response) => {if (err) console.error(err);else console.log(`Received: ${JSON.stringify(response)}`);
});

方案四:自动化测试工具链

// grpc-tester.ts 
import { Test } from '@nestjs/testing';
import { ClientGrpc } from '@nestjs/microservices';
import { HeroServiceClient } from './generated/hero';describe('gRPC Test Suite', () => {let client: HeroServiceClient;beforeAll(async () => {const module = await Test.createTestingModule({imports: [GrpcClientModule] // 自定义的gRPC客户端模块}).compile();const app = module.createNestMicroservice({});await app.init();client = module.get<ClientGrpc>('HERO_PACKAGE').getService<HeroServiceClient>('HeroService');});it('should return valid hero data', async () => {const response = await firstValueFrom(client.findOne({ id: 1 }));expect(response).toEqual({ id: 1, name: 'Superman' });});
});

关键问题解决方案总结


1 ) 证书链完整性验证

  • 使用OpenSSL诊断:openssl s_client -connect domain.com:50000 -CAfile certs/full-chain.pem
  • 确保包含从终端证书到根证书的完整链

2 ) NestJS版本兼容处理

// 解决"@grpc/grpc-js"版本警告 
import { credentials } from '@grpc/grpc-js';
const serverCredentials = credentials.createSsl(...);

3 ) Proto文件实时监控

// nest-cli.json
{"compilerOptions": {"assets": ["/*.proto"],"watchAssets": true }
}

关键问题总结

问题类型解决方案
Proto转TS类型VSCode插件快速生成 / TS-Proto工具链生成完整桩代码
SSL验证失败补全CA证书链,通过OpenSSL调试,确保证书包含完整根证书
客户端类型安全使用ClientGrpcProxy.getService()注入强类型服务接口
流式通信支持在.proto中定义stream参数,使用@GrpcStreamMethod处理双向流

最佳实践

  1. 证书管理:使用ACME.sh自动续签证书,确保证书链完整
  2. 类型同步:将proto文件生成步骤加入构建流程(npm scripts)
  3. 错误处理:在gRPC客户端拦截器中统一处理UNAVAILABLEPERMISSION_DENIED错误码

总结与注意事项

1 ) 核心优化点:

  • 协议一致性:.protopackageservice名称需与代码严格对应。
  • 证书链完整性:CA根证书必须包含所有中间证书,否则触发链验证失败。
  • 类型安全:通过ts-proto生成的接口确保RPC方法类型匹配。

2 ) 性能与安全平衡:

  • 非生产环境可使用createInsecure()快速测试。
  • 生产环境必须启用TLS,并定期轮换证书。

3 ) 扩展建议:

  • 流式通信:gRPC支持stream关键字实现双向流通信(如实时日志推送)。
  • 拦截器:NestJS的ClientInterceptor可统一处理认证/日志逻辑。

性能优化提示:在微服务集群中使用gRPC时,启用HTTP/2多路复用和连接池复用可提升30%以上的吞吐量。通过@grpc/grpc-jschannelOptions配置grpc.max_concurrent_streams参数优化并发流数量。

完整解决方案体现了协议层安全与开发效率的平衡,解决了从基础通信到生产级安全部署的全链路难题,为微服务架构提供了企业级通信基础。

http://www.dtcms.com/a/569133.html

相关文章:

  • 朴朴超市小程序分析
  • 济南物流公司网站建设金华建设工程网站
  • visual basic 从入门到精通 IT9网络学院VB编程系列培训教程
  • 厦门汽车充电站建设报备网站免费ppt模板下载中国风
  • 操作系统期中考试
  • 陕西建设集团韩城公司网站怎样创建基本的网站
  • 每日两题day33
  • 【解决】Failed to remove partition 1 from system: 设备或资源忙
  • Android通过SQL查询trace分析进程启动线程总数量
  • 超轻量级 AI 网安工具
  • 【C++】封装红黑树实现map和set容器(详解)
  • sscanf解析
  • 中专生实习找什么工作?
  • 车辆车身颜色特征识别
  • 【JUnit实战3_28】第十七章:用 JUnit 5 实测 SpringBoot 项目
  • 摆脱局域网束缚!用 DS File+cpolar 让 NAS 文件随用随取
  • 网站制作费电力行业做的好的招投标网站
  • 百度网址大全网站大全石家庄网页设计培训班
  • 设计模式学习(十二)状态模式
  • shell-基于k8s/docker管理容器、监控模型训练所消耗的最大CPU与最大内存脚本
  • Maven打包时指定输出路径、以时间戳命名包名和路径名,结合IDEA以指令脚本方式动态配置输出目录
  • PortSwigger
  • Doxygen入门指南:从注释到自动文档
  • Docker 部署 Elasticsearch 8.12 + Kibana + Nginx 负载均衡
  • yolo 训练 动态改变类别
  • SQL大表关联优化全攻略
  • 第五章:构建用户界面(UMG) - 游戏内HUD
  • CSS 雪碧图和 SVG 雪碧图的原理和区别
  • 网站底部代码特效邢台网红隧道
  • 网站降权表现营销型公司网站有哪些