本文档详细描述grpc是如何借助HTTP2实现的。您需要熟悉HTTP2规范。
生产规则使用ABNF语法。
以下是GRPC请求和响应消息流中的一般顺序
HTTP2要求reserved headers(以“:”开头的header)出现在所有其他标头之前。此外,其他的实现中应该在reserved headers之后立即发送Timeout,并且应该将Call-Definition头早于Custom-Metadata发送。
某些gRPC实现可能允许覆盖上面列表中的Path格式,但是强烈建议不要使用此功能。gRPC不会打破使用这种方式的用户,但是我们不乐意支持它,因为当路径不是上面显示的形式时,某些功能(例如,service config support)将不起作用。
如果省略了Timeout,则服务器应假定无限超时。客户端实现可根据其部署要求自由发送默认的最小超时。
如果Content-Type并非以“ application / grpc”开头,则gRPC服务器会返回415(不支持的媒体类型)。这可以防止其他HTTP/2客户机将状态为200 (OK)的gRPC错误响应解释为成功。(注:通过application / grpc来区分grpc请求和普通HTTP2请求)
Custom-Metadata是由应用程序层定义的任意键/值对集合。header名称以“ grpc-”开头,但未在此处列出保留以供将来的GRPC使用,并且这些保留header不应被应用程序用作Custom-Metadata。
请注意,HTTP2并不是使用随意的八字节序列来作为headers,二进制header值必须按照https://tools.ietf.org/html/rfc4648#section-4使用Base64进行编码。具体实现会接受填充的和未填充的值,并且发出未填充的值(???)。应用程序以“-bin”结尾来定义二进制headers。Runtime库会使用此后缀来检测二进制headers,并在发送和接收headers时使用base64编码和解码。
除了名称相同的header外,不保证Custom-Metadata header的顺序。重复的headers名称可以将其值以“,”作为分隔符,并在语义上等效。在Base64解码之前,这些Binary-Header是通过“,”来分隔的。
ASCII-Value不应在前或尾有空格。如果它包含前或尾空格,则可能会删除它。ASCII-Value定义的字符范围比HTTP更严格。具体的实现中要做到不会因接收到无效的ASCII-Value但在HTTP中为有效的字段值而抛错,但没有严格定义确切的行为:它们可能会丢弃该值或接受该值。如果接受了该值,则必须注意确保允许应用程序将值作为元数据回传也能够接受(注:就是双边都要能够接受)。例如,如果将元数据作为请求中的list提供给应用程序,则应用程序不会因为响应有相同的元数据列表而抛出错误。
服务器可能会限制请求header的大小,建议默认值为8 KiB。提倡在具体的实现中计算总header大小,例如HTTP / 2的SETTINGS_MAX_HEADER_LIST_SIZE:所有标头字段的总和 for each field the sum of the uncompressed field name and value lengths plus 32, with binary values’ lengths being post-Base64.(???)
Length-Prefixed-Message项放在DATA帧中传递
Compressed-Flag值为1表示二进制八字节序列的Message是使用Message-Encoding声明的方式压缩了。值为0表示未发生消息字节的编码。Compression contexts 在消息处理后就不保留了,在具体实现里必须为流中的每个消息创建一个新的上下文。如果省略了Message-Encoding标头,则Compressed-Flag必须为0。
对于请求,EOS(end-of-stream)是通过最后接收到的DATA帧上存在END_STREAM标志来判断的。在需要关闭请求流而且也没有数据要发送的情况下,具体的实现中会发送一个设置了该标志(END_STREAM)的空数据帧。
Response-Headers 和 Trailers-Only都是在一个HTTP2 HEADERS帧中传递。一般多数响应都具有headers和trailers的,但是允许使用Trailers-Only发送一个即时错误。即使状态代码是OK,也必须在Trailers中发送状态。
对于响应的EOS则是通过最后接收到的带有Trailer的HEADERS帧上END_STREAM标志的存在来指示的。
具体实现应该预料到有问题的部署会响应中发送非200个HTTP状态码,以及各种非grpc内容的类型,具体实现则必须生成Status和Status-Message,以便在发生这种情况时传播到应用层。
客户端可以限制Response-Header,Trailer和Trailer-Only的大小,建议每个默认值为8 KiB。
Status的值是十进制编码整数来作为ASCII字符串的,没有任何前导零。
Status-Message的值在是Unicode字符串描述的error信息,物理编码为UTF-8,后跟着 percent-encoding。 percent-encoding在RFC 3986§2.1中指定,尽管此处使用的形式具有不同的restricted characters。当解码无效值时,具体实现不得出错或丢弃该消息。在最坏的情况下,具体实现可能完全中止对status message的解码,以使用户会收到原始的百分比编码形式。或者,具体实现可以解码有效部分,同时保持损坏的%-encodings不变,或者用“?”或Unicode字符之类的字符来替换它们。
通过一个简单的一元调用来展示一下HTTP2成帧顺序
HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
DATA (flags = END_STREAM)
HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
content-type = application/grpc+proto
DATA
HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc
虽然协议本身不会用到user-agent来干啥,但是建议客户端提供结构化的user-agent string 来提供调用库的描述、版本和平台描述信息,这样方便在异构环境中进行问题诊断。建议库开发人员使用以下结构
User-Agent → "grpc-" Language ?("-" Variant) "/" Version ?( " (" *(AdditionalProperty ";") ")" )
比如:
grpc-java/1.2.3
grpc-ruby/1.2.3
grpc-ruby-jruby/1.3.4
grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile)
除非明确定义了,否则假定gRPC调用不是幂等的。特别的:
所有GRPC调用都需要指定一个内部ID。我们将使用HTTP2 stream-ids作为call标识符。注意:这些ID与打开的HTTP2会话相关,并且在处理多个HTTP2会话的进程中也不是唯一的,也不能用作GUID。
DATA帧边界与Length-Prefixed-Message边界没有关系,具体实现不应对其对齐方式进行任何假设。
在应用或者runtime运行RPC期间发生错误时, Status和Status-Message会包含在Trailers中
在某些情况下,消息流的帧可能已损坏,并且RPC运行时将选择使用RST_STREAM帧向其对等方指示此状态。RPC运行时的具体实现中会将RST_STREAM解释为流的立即完全关闭( immediate full-closure ),并且应将错误传播到调用应用程序层。
RST_STREAM错误代码到GRPC错误代码的映射关系如下:
当TLS与HTTP2一起使用时,HTTP2规范要求使用TLS 1.2或更高版本。它还对部署中允许的密码施加了一些其他约束,以避免已知问题以及需要SNI支持。本规范不能提出有意义的建议时,可以将HTTP2与其他专有的传输安全机制结合使用
GOAWAY Frame
由服务器发送给客户端,用来告诉客户端,服务端将不再接受相关连接上的任何新流。该帧包含服务器上次成功接受的流的ID。客户端应将上次成功接受的流之后启动的任何流视为UNAVAILABLE ,并在其他地方重试call。客户端可以自由地继续使用已经接受的流,直到它们完成或连接终止。服务器应在终止连接之前发送GOAWAY,以可靠地通知客户端服务器已经接受并且正在执行的工作。
PING Frame
客户端和服务器都可以发送PING帧,对等方必须通过准确地响应它们收到的内容。这用于断言该连接是否有效,并提供一种估算端到端延迟的方法。如果服务器发送的PING在运行时deadline内未收到响应,则服务器上所有未完成的call将以CANCELED状态关闭。客户端发送的PING过期将导致所有call以UNAVAILABLE状态关闭。请注意,PING的频率高度依赖于网络环境,具体实现可以根据网络和应用程序的要求自由调整PING频率。
Connection failure
如果在客户端上发生可检测到的连接失败,则所有呼叫将以UNAVAILABLE状态关闭。对于服务器,打开的呼叫将以CANCELLED 状态关闭。
通过protoc的代码生成工具,可以轻松的将protobuf声明的服务接口映射到GRPC。以下定义了要使用的映射。
参考:
https://stackoverflow.com/questions/56711666/why-does-grpc-uses-length-prefixed-messages
https://www.cnblogs.com/ghj1976/p/4581426.html