原文链接:https://zhuanlan.zhihu.com/p/...

rpc框架概述

微服务架构是近期比较流行的架构,知乎也实现了服务化。要实现服务化,则首先要解决各个服务之间的通信问题。那么就会面临数据序列化、反序列化、连接管理、收发线程、超时处理等问题。如果自己实现一套这样的机制,不但重复劳动,性能和效率也难以保证。rpc框架的出现解决了这些问题,让调用者不必关心底层细节。目前主流的rpc框架有Apache Thrift、gRPC、Netty等。Apache Thrift和gRPC都是跨语言的rpc框架,他们采用了IDL来描述数据类型和接口,使用编译器编译出特定语言的代码从而实现跨语言的rpc。本文主要介绍gRPC的规范及实现。

gRPC实现

rpc框架一般基于tcp或者http协议实现。基于http的rpc框架有许多优点,HTTP/1.x协议简单明了,是目前最流行的应用层协议,有着非常成熟且完善的各种基础设施,如负载均衡、监控、代理等,适用性广泛,各个设备系统均有实现。但是缺点也很明显,就是HTTP/1.x采用的是文本协议,解析速度慢,带宽占用高。而且request/response的通信方法导致整体效率不高。gRPC基于HTTP2协议,HTTP2 使得grpc 能够更好的适用于移动客户端和服务端通信的使用场景,并且连接多路复用也保证了RPC 的效率。grpc 的协议设计上很好的使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其他的控制信息则用Header 表示。

HTTP2基本概念

流(Stream)

流是服务器和客户端在HTTP/2连接内用于交换帧数据的独立双向序列,逻辑上可看做一个较为完整的交互处理单元,即表达一次完整的资源请求-响应数据交换流程;一个业务处理单元,在一个流内进行处理完毕,这个流生命周期完结。

特点如下:

  • 一个HTTP/2连接可同时保持多个打开的流,任一端点交换帧
  • 客户端、服务端双方都可以建立流,流也可以被任意一方关闭
  • 流的标识符自然数表示,1~2^31-1区间,有创建流的终端分配
  • 客户端发起的流使用奇数流ID,服务端发起的使用偶数,ID为0、1的流保留
  • HTTP/2连接上传输的每个帧都关联到一个流,一个连接上可以同时有多个流
  • 同一个流的帧按序传输,不同流的帧交错混合传输

帧(Frame)

HTTP/2抛弃HTTP/1的文本协议改为二进制协议,HTTP2的基本传输单元为帧,每个帧都从属于某个流。

帧的格式如下:

  • - Length: Payload 长度
  • - Type: 帧类型
  • - Stream identifier:所属流ID
  • - Frame Payload: 依帧类型而不同

帧的类型有:

  • - HEADERS 对应HTTP/1的 Headers
  • - DATA 对应HTTP/1的 Body
  • - CONTINUATION 头部太大,分多个帧传输(一个HEADERS+若干CONTINUATION)
  • - SETTINGS 连接设置
  • - WINDOW_UPDATE 流量控制
  • - PUSH_PROMISE 服务端推送
  • - PRIORITY 流优先级更改
  • - PING 心跳或计算RTT
  • - RST_STREAM 马上中止一个流
  • - GOAWAY 关闭连接并且发送错误信息

流是为了实现多路复而提出的逻辑概念,在一个连接上可以同时存在多个流。而流是由一个个的帧组成,在一个流里面的帧是有序的,多个流之间的帧可以混杂在一起传输。

gRPC over HTTP2

gRPC在HTTP2的基础上定义了request和response的规范。使用header帧描述meta信息如超时时间、payload的压缩算法等等。使用data帧传输具体的rpc参数和返回结果。

request header

scheme: http或者https

method: 固定为POST

path: 服务名/rpc方法名

content-type: 目前取值都是application/grpc+proto,或json或其他自定义协议

grpc-encoding 可以有gzip, deflate, snappy 等取值,表示采用的压缩方法。

grpc-timeout 表示调用的超时时间,单位有Hour(H), Minute(M), Second(S), Millisecond(m), Microsecond(u), Nanosecond(n) 等。

response header

response有两种,一般情况下response都要包含有header帧、数据帧和trailer。如果服务端发生了错误,则可以只返回一个trailer即 Trailers-Only 。

Response-Headers 主要包括 HTTP-Status,Content-Type 以及 Custom-Metadata 等。Trailers-Only 也有 HTTP-Status ,Content-Type 和 Trailers。Trailers 包括了 Status 以及 0 或者多个 Custom-Metadata。

HTTP-Status 就是我们通常的 HTTP 200,301,400 这些,很通用就不再解释。Status 也就是 gRPC 的 status, 而 Status-Message 则是 gRPC 的 message。Status-Message 采用了 Percent-Encoded 的编码方式,具体参考这里

如果在最后收到的 HEADERS frame 里面,带上了 Trailers,并且有 END_STREAM 这个 flag,那么就意味着 response 的 EOS。

Request例子:

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)
<Delimited Message>

Response例子:

HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip

DATA
<Delimited Message>

HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc

从图中可以看出,客户端的每次rpc调用都发起了一个流,然后在这个流中发送header帧和数据帧,而服务端的返回结过结果也是使用同一个流进行传输。

虽说gRPC使用HTTP/2协议,很多浏览器也开始支持HTTP2,但是目前还不能使用浏览器作为客户端访问gRPC服务。因为gRPC的response在数据帧之后还有一个trailer header帧(这个帧内包含了grpc-status和grpc-message头),这会导致chrome的崩溃。为了解决这一问题一个新项目grpc-web正在开发之中,还处于early access阶段,参见这里。grpc-web在grpc over HTTP2的基础上修改和新增了一些规范,使得能够通过浏览器和JavaScript调用gRPC服务,参见这里

Nginx目前也无法代理gRPC服务。虽然nginx从1.9.5版本开始支持HTTP2,但是还不支持反向代理HTTP2的upstreaming。参见这里

目前可行的通过浏览器访问grpc的方法是使用一个gateway,在grpc服务和浏览器之间作为中介,接受浏览器的Restful请求转换成grpc标准的请求发送到服务端。参见这个项目


byte
106 声望13 粉丝