原文连接: https://zhuanlan.zhihu.com/p/...
微服务架构是近期比较流行的架构,知乎也实现了服务化。要实现服务化,则首先要解决各个服务之间的通讯问题。那么就会面临数据序列化、反序列化、链接管理、收发线程、超时处理等问题。若是本身实现一套这样的机制,不但重复劳动,性能和效率也难以保证。rpc框架的出现解决了这些问题,让调用者没必要关心底层细节。目前主流的rpc框架有Apache Thrift、gRPC、Netty等。Apache Thrift和gRPC都是跨语言的rpc框架,他们采用了IDL来描述数据类型和接口,使用编译器编译出特定语言的代码从而实现跨语言的rpc。本文主要介绍gRPC的规范及实现。html
rpc框架通常基于tcp或者http协议实现。基于http的rpc框架有许多优势,HTTP/1.x协议简单明了,是目前最流行的应用层协议,有着很是成熟且完善的各类基础设施,如负载均衡、监控、代理等,适用性普遍,各个设备系统均有实现。可是缺点也很明显,就是HTTP/1.x采用的是文本协议,解析速度慢,带宽占用高。并且request/response的通讯方法致使总体效率不高。gRPC基于HTTP2协议,HTTP2 使得grpc 可以更好的适用于移动客户端和服务端通讯的使用场景,而且链接多路复用也保证了RPC 的效率。grpc 的协议设计上很好的使用了HTTP2 现有的语义,请求和响应的数据使用HTTP Body 发送,其余的控制信息则用Header 表示。nginx
HTTP2基本概念git
流(Stream)github
流是服务器和客户端在HTTP/2链接内用于交换帧数据的独立双向序列,逻辑上可看作一个较为完整的交互处理单元,即表达一次完整的资源请求-响应数据交换流程;一个业务处理单元,在一个流内进行处理完毕,这个流生命周期完结。web
特色以下:算法
帧(Frame)chrome
HTTP/2抛弃HTTP/1的文本协议改成二进制协议,HTTP2的基本传输单元为帧,每一个帧都从属于某个流。json
帧的格式以下:api
帧的类型有:浏览器
流是为了实现多路复而提出的逻辑概念,在一个链接上能够同时存在多个流。而流是由一个个的帧组成,在一个流里面的帧是有序的,多个流之间的帧能够混杂在一块儿传输。
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标准的请求发送到服务端。参见这个项目