【技术创作101训练营】剖析 gRPC

2021-01-18 18:01:15 浏览数 (1)

grpc_0117.pptx

剖析gRPC演讲稿

各位好,今天的主题是剖析gRPC, 我们在实际工作中大量的应用gRPC做服务之间的调用。经常用写一些proto文件,用protoc把我们的proto文件生成相应语言的代码,但大多数人很少关注protoc生成的相应语言代码里都有什么内容,由于他的简单易用,我们基本上读一下文档,写一个小例子就能快速入门使用他。但一问你他的原理是什么,为什么生成的相关语言的代码里会有一些我们用不到的字段,比如生成的 file_protos_xxxxxxx_proto_rawDesc 是一个[]byte ,他是做什么的?还有客户端在进行调用的时候是如何定位到具体方法的,我没有没有想过?现在大部分服务是跑在k8s里服务的发现和治理我们都不用关心,如果不用k8s我们怎么做服务的注册和发现,gRPC为我们提供了什么方案让我们集成服务的注册和发现呢?如果你心中有疑问,那您今天就来着了,我今天主要讲的就是这些基础的内容,其中也会有一些代码例子,分析实际具体的代码会让大家能更深入的了解gRPC的原理

分享大概1小时左右,好下面就开始进入主题。

什么是Rpc & gRPC

在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统

gRPC是一种现代化开源的高性能RPC框架,能够运行于任意环境之中。最初由谷歌进行开发。它使用HTTP/2作为传输层。

为什么要用gRPC

使用gRPC,我们可以一次性的在一个.proto文件中定义服务并使用任何支持它的语言去实现客户端和服务端,反过来,它们可以应用在各种场景中,gRPC帮你解决了不同语言及环境间通信的复杂性。使用protocol buffers还能获得其他好处,包括高效的序列号,简单的IDL以及容易进行接口更新。使用gRPC能让我们更容易编写跨语言的分布式代码。

gRPC 的核心

gRPC的两个核心一个是http2一个是protobuf

http2

http2做为gRPC的核心之一,他有哪些好处呢。

* 多路复用(Multiplexing),允许同一个连接发起多重请求-响应。一个request对应一个stream并分配一个id,同时可以有多个stream,每个streamframe可以随机的混杂在一起,接收方可以根据 stream idframe归属到不同的request

* Header压缩,http1.x都是用的明文,http2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。高效的压缩算法可以很大的压缩header,减少发送包的数量从而降低延迟。

* 流量控制,http2.0flow control是类似receive window的做法,数据的接收方通过告知对方自己的flow window大小表明自己还能接收多少数据。只有Data类型的frame才有flow control的功能。其他特点,这里就不说了,有时候大家可以查一下相关资料

当然还有很多其他的优点,这里就不一一说了。

我们在做长链接服务的时候一定针会做的一件事就是确定一个数据包的格式和数据长度,如果数据包太大还要处理大包拆分包的逻辑,这些http2都有并且他还有其他的好处。

Protocol Buffers

protobufjson不香吗?一般得到的回答都是protobufjson快,但是快在了哪呢?等会儿我会详细说明。

Protocol Buffers 是一种与语言、平台无关,可扩展的序列化结构化数据的方法,常用于通信协议,数据存储等等。相较于 JSONXML,它更小、更快、更简单,因此也更受开发人员的青眯

ppt这有语法

protobufgrpc默认的传输协议,当然你也可以自定义比如使用json等。大多数人提到为什么要使用

定义完 proto文件后,生成相应语言的代码

代码语言:txt复制
protoc --proto_path=. --go_out=plugins=grpc,paths=source_relative:. xxxx.proto

--proto_path 或者 -I 参数用以指定所编译源码(包括直接编译的和被导入的 proto 文件)的搜索路径

--go_out 参数之间用逗号隔开,最后用冒号来指定代码目录架构的生成位置, eg:--go_out=plugins=grpc,paths=import:. 。注意一下 paths 参数,他有两个选项,importsource_relative 。默认为 import ,代表按照生成的 go 代码的包的全路径去创建目录层级,source_relative 代表按照 proto 源文件的目录层级去创建 go 代码的目录层级,如果目录已存在则不用创建。

看一下pptStudent结构体,转换成JSON和样子,我们一眼就能看明白,JSON是给人读的。

转换成protobuf 数据格式是一串二进制,我们是不能一下子知道这一串二进制是什么意思。

再看一下protobuf支持的几种数据类型(wire types),protobuf是语言无关的,所有支持protobuf的库序列化时最终都会转换成这几种类型,反序列化时会转换成相应语言的类型。

字段的 Index和类型

Protobuf 把一个字段的 index 和类型放在了一起

(field_number << 3) | wire_type

eg: 0 000 1000 首位为标识位,index: 1 后三位为wire_type:0 eg: 10010 index: 2 wire_type: 2

Varint 内存存储方式

Varint数据类型,最高位(msb)标志位

* 1说明后面还有byte

* 0说明后面没有byte

7个Bit位存储数值

ppt这里的1300在内存的存储方式

Length-delimited

字符串和数组类型,就是length-delimited我们看一下字符串内存的展示方式,protobuf一个汉字占3byte

“孙悟空”内存的数据

代码语言:txt复制
11100101 10101101 10011001 11100110 10000010 10011111 11100111 10101001 10111010

“孙悟空”前面的一个byte1001就是字符串的长度。

gRPC的调用方式

gRPC 目前有四种

一. Unary RPC:一元 RPC,发送 RPC 请求,等待同步响应,得到回调后返回响应结果

二. Server-side streaming RPC:服务端流式 RPC

三. Client-side streaming RPC:客户端流式 RPC

四. Bidirectional streaming RPC:双向流式 RPC

需要注意一点:只能是客户端发起请求,目前grpc 没有推送功能。

ppt里每一种调用方式的图片,就能很方便的理解。

gRPC 方法调用流程

我们在编写客户端代码时,能非常方便的调用服务端的代码

代码语言:txt复制
  rev, err := client.StudentByID(ctx, req)

在通信过程中,是如何正确的访问服务端的方法的?

protoc 为我们做的工作不只是把 message 转换成相应语言的数据结构,而且把路由信息也生成了,直接看代码来讲解。

代码语言:txt复制
scheme:    "http"

method:    "/api.StudentSrv/StudentByID"

host:      "localhost:10001"

Intercepto

grpc服务端提供了interceptor 拦截器功能,类似gin 里的middleware ,可以在服务端接收到请求时,对请求中的数据做一些处理后再处理具体业务。

ppt用的就是https://github.com/grpc-ecosystem/go-grpc-middleware库里的方法。

gRPC 服务发现&负载均衡

常用的就两种方式

* Proxy Model,可以使用nginx或者traefik等代理软件来实现。

* Client Model,客户端自己实现

ppt里的图,是用nginx来做的。

最常用就是客户端自己实现,用zk或者etcd等来实现。原理很简单,就是服务端注册,客户端resolverwatch更新地址列表等。今天的例子是用etcd实现的.

etcd 简介

etcd是一个高可用的键值分布式存储系统,主要用于共享配置和服务发现

服务注册:主要思路是创建一个lease租约,put一个前缀的Key(方便服务发现时根据前缀取key对应的值);然后通过keepAlive续约,并监听keepAlive通道保持在线,如果不监听,或者程序崩溃,etcd会删除这个租约上的key

服务发现:通过前缀取出key对应的values;然后启动一个监听服务监听key的变化.

etcd已经为我们做了一些工作,这是官网的说明https://etcd.io/docs/v3.2.17/dev-guide/grpc_naming/,我们要做的主要是,实现ResolverBuilder两个接口,看一下我们的例子。

服务端,我们没有指定具体的端口,是让系统随机给分的,这样我们就可以把启动的实例的ip port保存到etcd,服务端还有一点,就是用了拦截器,这里打印出了当前的端口,这样,我们在做测试的时候就能看到是哪个服务进行的消息处理。

客户端,我们连的是etcd,调用gRPC的注册方法,把我们自定义的Builder进行注册,这个接口的方法Build返回的就是我们自定义的Resolver,里面把从etcdwatch到的数据通过调用GrpcUpdateState来更新集群的地址。

到此为止就是这次分享的主要内容,还有最后一页就是分享几个常用的工具。

https://github.com/uw-labs/bloomrpc 图形工具还是很推荐大家用的,写好服务后,这个工具能方便的进行rpc调用。

也可以用这个库 https://github.com/fullstorydev/grpcurlcurl 一样访问 grpc

还有几个库,有兴趣的可以点开看看。

谢谢大家,如果还有什么问题可以说出来,一起讨论。

0 人点赞