tRPC-GO

tRPC-go

https://trpc.group

先说一说RPC

remote procedure call protocol 远程调用协议

客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。

C/S 架构

当客户端发送的请求消息到达服务器时,服务器上的操作系统就将他传递给服务器存根(server stub), 服务器存根是客户存根在服务器端的对应程序和真正实现,用来将通过网络输入的请求转化为本地过程调用 服务器存根一直在等待客户端的调用,处于阻塞状态,等待消息输入。

RPC流程

  1. 客户端进程以正常方式调用客户存根
  2. 客户存根生成一个消息。然后调用本地操作系统的网络通信模块,存根进入阻塞状态。
  3. 客户端操作系统将网络消息发送给远程操作系统。
  4. 远程操作系统将网络消息交给服务器存根。
  5. 服务器存根从网络消息将参数提取出来,而后调用服务器存根。
  6. 服务器执行相应的程序,操作完成后将结果返回给服务器存根。
  7. 服务器存根打包消息,调用本地操作系统。
  8. 发送给客户端操作系统。
  9. 客户端操作系统将消息交给客户端存根,存根从阻塞状态回复,进入运行状态。
  10. 客户端存根从消息中提取结果,返回调用的客户端。

  11. 过程中需要考虑的问题:
  • 参数传递
  • 通信协议
  • 出错
  • 超时处理

一些说明

  1. 既然是协议就只是一套规范。那么就需要有人遵循这套规范来进行实现 典型的RPC协议 Dubbo Thrift gRPC Hetty
  2. 网络协议和网络io模型 对其透明 既然RPC的客户端认为自己是在调用本地对象。那么传输层使用的是TCP/UDP还是HTTP协议,又或者是一些其他的网络协议它就不需要关心了。
  3. 信息格式对其透明.
  4. 应该有跨语言能力, 调用方也不清楚远程服务器的应用程序是使用的什么语言与你性的,对于调用方来说,无论服务器方使用的什么语言,本次调用都应该成功。并且放回置应该按照调用方程序语言所能理解的形式表达。

IDL interface definition language

protocol buffer language .proto version 3

syntax = "proto3"   // 必须
message SearchRequest{
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

tRPC 能做什么

  • 搭建多个端口支持多个协议(一个端口对应一个协议)
  • 搭建消息队列消费者服务 提供消息队列生产者客户端发送消息
  • 搭建本地定时器 分布式定时器服务
  • 搭建流式服务 实现push 文件上传 消息下发等流式模型
  • 访问各种私有协议后端服务
  • 通过trpc工具生成桩代码和服务模板

需要基础知识

  • Go语言基础
  • context原理
  • RPC概念
  • tRPC术语
  • proto3知识

安装 proto

protobuf 一种与平台语言无关 且可扩展的序列化结构数据的描述语言

接口描述语言 Interface Description Language IDL

比较 grpg restful
规范 .proto 可选OpenAPI
协议 HTTP/2 任意版本HTTP
有效载荷 proto 小、二进制 json 大 宜读
浏览器
流传输 客户端 服务端 双向 客户端 服务端
代码生成 OpenAPI 第三方/工具

tRPC是由全公司OTeam共建的一款高性能,多语言,插件化,平台无关,开源协同,对接存量,面向未来,致力统一的远程过程调用开发框架。你可以使用tRPC:

  • 内置了统一tRPC通信协议, 通过与多语言特性的结合, 为复杂产品形态下的多技术栈提供了通用解决方案
  • 快速构建的支持多协议服务, 协议包括通用协议和公司内部私有协议
  • 快速搭建流式服务,实现push,文件上传,消息下发等流式模型
  • 通过tRPC插件生态, 业务自己选择服务治理体系, 实现和存量系统对接
  • 基于框架接口进行二次开发, 包括: 通信协议, 插件, 存储接口, 拦截器等定制开发
  • 通过tRPC测试工具和框架自动生成mock代码, 使服务的测试更简单, 更便捷

路由标识

通常路由标识的命名格式和其所对接的名字系统或服务运营系统相关,

例如在PCG 123系统中,路由标识是由.号分割的四段字符串:”trpc.{app}.{server}.{service}”来表示。在后续tRPC文档中,大部分示例都推荐采用 “trpc.{app}.{server}.{service}” 的命名格式来定义服务名。其中app, server, service的含义为:

app: 应用名. 标识一组服务的一个小集合, 开发者可以根据需要自己定义, 通常表示实现某个业务系统名称

server: 提供服务的进程名称, 一个 server 必须属于某个app, app 下的 server 名称都具备唯一性.一个server代表一个独立的程序, 绑定至少一个 ip, 实现至少一个service

service: 服务提供者,提供接口规范供客户端调用。

从网络通信的角度讲, 每个service对应一个IP + 端口 + 协议。

接口标识

在解决了服务寻址后, 我们再看看在 通信报文中如何来识别一个RPC调用 . RPC调用接口也需要一个全局唯一标识, 我们把它称为接口标识 或 RPCName, 它由 “/{package}.{proto service}/{method}” 三段组成, 各个字段的含义如下:

package: 包名,tRPC对于服务的定义使用了protobuf的语法和概念. 这里的”package”对应于protobuf中的package, 可以理解为一个支持多级的名字空间, 用来防止接口命名冲突

proto service: 协议服务名,对应于protobuf中的service, 是一组RPC方法的集合,它是对接口的一个逻辑分组。一个server内允许定义多个proto service

method: 方法或接口,对应于protobuf中的rpc方法, 包括方法的名称,请求和响应报文的参数类型, 每个method必须且只能属于一个proto service. 一个proto service允许定义多个method

多种模式

  1. 单服务模式
  2. 单服务多协议
  3. 多服务多协议

tRPC协议

能力 诉求
高性能 协议的方案选型和具体实现上需要考虑性能问题
透传用户自定义元数据 协议上应该支持用户设置自定义元数据和透传的能力
调用链信息的透传 协议上应该提供服务调用链信息透传的能力
请求超时控制 请求可以设置超时时间
认证和鉴权 协议上应该提供用户设置自定义鉴权信息的能力
流式请求 协议上应该能解决大数据包传输的问题,和满足边请求边应答的流式场景
染色key 协议上应该提供染色key在服务间透传的能力
支持压缩 协议上应该支持可选择对业务编码后的数据进行压缩
插件化支持多种编码协议 协议上支持业务数据的序列化类型应该是多样的,比如:protobuf/jce/json/…
向前兼容 协议设计和实现上应具有可扩展性
RPC调用方式 远程调用像调本地接口一样

https://trpc.group/zh/docs/what-is-trpc/trpc_protocol_design/

框架核心 三部分组成

通信层 负责数据的传输和协议的编解码. 框架内置支持tcp/udp/quic等通信协议, 传输协议采用基于PB的tRPC协议来承载RPC调用, 支持通过codec插件来使用其它传输协议

服务治理层 负责将服务治理功能抽象成插件组件, 采用插件化架构, 通过调用插件和外部服务治理系统完成对接, 实现服务发现,负载均衡,监控,调用链等服务治理功能

调用层 封装服务和服务代理实体, 提供RPC调用接口, 支持业务用同步,异步,单向以及流式调用等方式进行服务间调用

在这张图中,我们新增了一层Filter层(拦截器),其主要目的是采用AOP的思想,把业务个性化的需求(比如:校验校验、请求回放、故障注入等)、以及服务治理的大部分功能(比如:监控指标上报,调用链跟踪,远程日志,鉴权等)以横切关注点的方式,插入到请求/响应处理的流程中,通过这样的设计来增强框架的可扩展性。

同时系统对每一层进行模块化拆分,采用插件化的实现,框架通过基于接口编程的思想,串联RPC的全流程。对于一些模块,框架也采用了更细粒度的模块拆分。比如:Selector模块被细分为服务发现、服务路由、负载均衡和熔断等子模块,Codec层也被细分为编解码、序列化、压缩三个子模块。

通过上面整体的分层设计,具体模块的插件化实现和细粒度的模块拆分,使框架具备很强的扩展性和开放性。业务可以灵活替换插件,实现与不同系统的对接,也可以进行业务个性化定制能力实现。