站在巨人的肩膀上:gRPC通过HTTP/2构建云原生时代的通信标准

gRPC:云原生时代的通信标准

gRPC将HTTP/2作为其传输层,并在此基础上定义了自身的通信语义。gRPC支持四种服务方法类型,它们都映射到HTTP/2的流模型上。

1)Unary RPC: 客户端发送单个请求,服务器返回单个响应(类似传统请求-响应)。

2)Server Streaming RPC: 客户端发送单个请求,服务器返回一个消息序列(流)。

3)Client Streaming RPC: 客户端发送一个消息序列(流),服务器返回单个响应。

4)Bidirectional Streaming RPC: 客户端和服务器都可以独立地发送一个消息序列(流)。

请求 (Request) 结构

gRPC的Request 包含请求头(Request Headers),请求体(Request Body)和EOS(end-of-stream)。

请求头使用 HTTP/2的headers, 使用HEADERS和CONTINUATION帧派发。请求头有Call-Definition和Custom-Metadata。其中grpc-前缀为 gRPC自己保留。

bash 复制代码
HEADERS (flags = END_HEADERS)
// HTTP 方法。对于 gRPC,:method标头始终为POST。
:method = POST 
// HTTP 协议。如果启用了 TLS,则协议设置为https,否则为http。
:scheme = http 
// 终端路径。对于 gRPC,此值构造为"/${包名}.${服务名}/${接口名}"。
:path = /ProductInfo/getProduct 
// 目标 URI 的虚拟主机名。
:authority = abc.com 
// 不兼容代理的检测。对于 gRPC,该值必须是"trailers"。
te = trailers 
// 调用超时时。如果未指定,server端应假定无限超时。
grpc-timeout = 1S 
// 内容类型。对于 gRPC,内容类型应以application/grpc。
content-type = application/grpc 
// 消息压缩方式。包括identity、gzip、deflate、snappy及自定义压缩方式。
grpc-encoding = gzip 
// 可选的请求头,用于访问有安全限制的终端服务。
authorization = Bearer xxxxxx

请求体使用DATA帧派发,请求体是长度前缀消息。它有一个 Compressed flag 用来表示message 是否压缩,为1表示采用了压缩算法(具体的压缩算法在HEADERS帧中定义)。后面跟着四字节的 message length 以及实际的 message。

EOS会在DATA帧里面带上了END_STREAM这个flag。用来表示请求消息的结束。

bash 复制代码
DATA (flags = END_STREAM)
<Length-Prefixed Message>

响应 (Response) 结构

gRPC的Response包含响应头(Response-Headers),响应体(Response Body)和 Trailers。

bash 复制代码
HEADERS (flags = END_HEADERS)
// 标识 HTTP 请求的状态。
:status = 200 
// 消息压缩类型。包括identity、gzip、deflate、snappy和自定义类型。
grpc-encoding = gzip 
// 内容类型。对于gRPC,content-type应该设置为application/grpc。
content-type = application/grpc 

发完响应头,使用DATA帧派发响应体。响应体是长度前缀消息。

bash 复制代码
DATA
<Length-Prefixed Message>

结束标志不与数据帧一起发送,而是作为单独的头部发送,称为Trailers。 如果遇到了错误,也可以直接返回 Trailers-Only。

Trailer是一种特殊的元数据,通常包含有关执行的状态信息,例如状态码( grpc-status)和状态消息(grpc-message)

gRPC之所以要用单独的Trailers来标志响应结束。是因为在streaming模式下,所有消息没有传输完成之前,gRPC也不知道要传什么样的grpc-status 。

bash 复制代码
HEADERS (flags = END_STREAM, END_HEADERS)
// gRPC 状态代码
grpc-status = 0 # OK 
// 错误描述。可选的,仅在处理请求出现错误时设置。
grpc-message = xxxxxx 

服务描述

gRPC 利用 .proto 文件中的 service 来定义 RPC 接口。

bash 复制代码
// 文件: demo/hello/greeter.proto
syntax = "proto3";

package demo.hello;

// Greeter 服务定义
service Greeter {
  // SayHello 方法定义
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// 请求消息结构
message HelloRequest {
  string name = 1;
}

// 响应消息结构
message HelloReply {
  string message = 1;
}

在上述 .proto 文件中,定义了一个名为 Greeter 的服务,它包含一个名为 SayHello 的方法。该方法接收 HelloRequest 消息并返回 HelloReply 消息。gRPC工具链会基于此IDL生成各种语言的客户端存根(stub)和服务端骨架(skeleton)。开发者通过调用存根上的方法,就如同调用本地方法一样,gRPC框架会处理底层的序列化、网络通信和方法分发。

一个 gRPC 定义包含三个部分,包名、服务名和接口名,连接规则如下:

bash 复制代码
/${包名}.${服务名}/${接口名}

SayHello的包名是demo.hello,服务名是Greeter,接口名是SayHello,所以对应的路径就是 /demo.hello.Greeter/SayHello。

bash 复制代码
HEADERS (flags = END_HEADERS) 
:method = POST
:scheme = http
:path = /demo.hello.Greeter/SayHello
:authority = grpc.demo.com
content-type = application/grpc

DATA (flags = END_STREAM) 
<Length-Prefixed Message>

gRPC基于这样的一个设计理念:定义一个服务,及其被远程调用的方法(方法名称、入参、出参)。开发者可以像调用本地方法一样,使用gRPC客户端存根(stub)调用远程机器上 gRPC 服务的方法。gRPC 的客户端和服务端都可以用任何支持 gRPC 的语言来实现,例如一个 gRPC 服务端可以是 C++语言编写的,以供 Ruby 语言的 gRPC 客户端和 JAVA 语言的 gRPC 客户端调用,如下图所示。

未完待续.

很高兴与你相遇!如果你喜欢本文内容,记得关注哦!

相关推荐
阿里云云原生4 分钟前
从这张年度技术力量榜单里,看见阿里云从云原生到 AI 原生的进化能力和决心
云原生
阿里云云原生35 分钟前
2025 智能体工程现状
云原生·llm
Lucifer三思而后行35 分钟前
看来 Oracle 还是听劝的!
http
是一个Bug1 小时前
云原生架构
云原生·架构
fredricen1 小时前
Luci操作创龙(Tronlong)T113-i开发板GPIO
rpc·web·openwrt·luci
刺客xs2 小时前
TCP网络通信
网络·网络协议·tcp/ip
爬山算法2 小时前
Netty(26)如何实现基于Netty的RPC框架?
网络·网络协议·rpc
程序猿编码3 小时前
轻量高效的HTTP/HTTPS代理:SSL拦截与流量管控核心解析
http·https·ssl·代理
JIes__3 小时前
网络协议——网络层协议
网络协议
网安INF3 小时前
SSL/TLS体系结构
网络·网络协议·网络安全·ssl