极客时间——深入浅出grpc

时间:Dec. 20, 2020 分类:

目录:

0 gRPC入门

RPC框架的目的是让远程调用变得简单和透明,负责屏蔽底层的传输方式、序列化方式和通信细节。服务可以通过调用本地端口的方式调用远程服务提供者

业界主流的rpc框架分为三类

  1. 多语言的rpc框架,例如grpc,Thrift
  2. 只支持特定的语言的rpc框架,例如Motan
  3. 支持服务治理等服务化特性的分布式服务框架,底层依然是RPC框架,例如dubbo

随着微服务发展,语言的中立性原则构建微服务称为主流趋势

grpc是一个高性能,开源和通用的RPC框架,面向服务端和移动端,基础HTTP/2设计,支持C,Java和Go

grpc的特点

  1. 语言中立
  2. 基于IDL文件定义服务,通过proto3工具生成指定语言的数据结构,服务端接口以及客户端Stub,和RPC使用动态代理和反射机制进行调用对比性能更好
  3. 通信协议基于HTTP/2设计,支持双向流、消息头压缩、单TCP多路复用、Server端推送等特性,使得支持移动端更加省点和节省网络流量(这里的Server端推送是对响应的持续推送)
  4. 序列化支持ProtocolBuffer

1 服务端创建和调用原理

服务端创建流程

  1. 创建HTTP2Server
  2. 将调用的服务端接口实现注册到内部的registry,RPC调用的时候,可以根据RPC请求中定义的服务信息查询到服务接口实现方法
  3. 创建gPRCServer,聚合各种Listener,用于RPC消息的统一调度和处理

服务端调用流程

  1. gRPC接收请求
  2. gRPC消息头和消息体的处理
  3. 内部路由和调用
  4. 响应消息发送

gRPC消息头和消息体的处理

  1. Header的Content-Type必须为"application/grpc"
  2. Header的URL中提取接口和方法名,例如"helloworld.Greeter/SayHello"
  3. 将Header转为gRPC的内部metadata
  4. 创建stream对象,实现协议消息处理,并完成消息上下文和grpc监听器的创建
  5. 如果Header的grpc-timeout,会启动延时任务

内部路由和调用

  1. 将body序列化为IDL定义的请求参数对象
  2. 根据Header中的方法到注册中心查询对应的服务定义
  3. 调用对应的注册方法

2 客户端创建和调用原理

Client调用流程

  1. 发起RPC调用
  2. 进行域名解析获取地址列表,使用负载策略,选择一个具体的gRPCServer实例
  3. 如果与Server端没有可用的连接,则发起HTTP/2连接
  4. 对请求消息做protobuf做序列化,通过HTTP/2的stream发送给gRPCServer
  5. 接收到Server响应之后,使用protobuf反序列化
  6. 唤醒Client线程获取RPC响应

http2在tcp连接的时候会进行协议的协商版本标识为2种

  • 基于TLS的HTTP2,即HTTPS,为h2
  • 直接使用TCP的HTTP2,即HTTP,为h2c

http2的连接创建方式

  • 协商升级
  • 直接连接

如果不知道服务端是否支持HTTP2,可以用HTTP1.1进行协商,报文如下


GET / HTTP/1.1
Host: 127.0.0.1
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>

如果不支持HTTP2则会按照HTTP1.1响应,双方通过HTTP1.1通信


HTTP/1.1 200 OK
Content-Length: 28
Content-Type: text/css

body...

如果服务器支持HTTP2,协商成功返回101状态码,通知Client升级为HTTP2通信


HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c

[ HTTP/2 connection...

101响应之后,Server端发送SETTINGS帧作为连接序言,Client接收到101响应之后也会发送连接序言


PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
SETTINGS帧

Client发送了连接序言之后,可以不等待Server的SETTINGS帧直接发送业务请求的Frame

如果知道对端支持HTTP2,则可以在TCP连接完成直接发送序言

负载策略,支持两种

  • 使用第一个
  • rr