golang随手记-grpc

Posted 假装懂编程

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang随手记-grpc相关的知识,希望对你有一定的参考价值。

RPC

什么是rpc框架?

简单来说服务A调用服务B,不需要显式的发起http或者tcp的请求,只需要调用本地函数,本地函数再通过http或tcp发起调用,然后数据返回。对于服务A来说,它并不关心函数内部是如何实现的网络调用,因此实现一个rpc框架需要:

  1. 因为服务多,函数多。服务A 可能会调用服务B的f1,也可能调用服务B的f2。一般一个func会对应的一个ID,对于服务B提供的func来说,每个func应该都是唯一的。ID可以数字映射,也可以是字符串。

  2. func接收的参数得传递过去,这就涉及到序列化。序列化可以把id和参数一起传递过去,可以是json,也可以xml。

  3. 序列化好的数据,通过网络传输过去,可以是http协议,也可以是tcp协议等。

  4. 服务B监听的socket,拿到数据包。

  5. 按照事先约定好的序列化方案,反序列化。

  6. 反序列化,得到执行的func的ID和params,通过ID映射找到真正执行的func

  7. 把params注入到func中,执行。

  8. 结果序列化,通过网络返回

  9. 服务A按照同样的方式解析数据,拿到结果。


RPC的好处是把远程调用封装成本地调用,隐藏了底部的通信细节,对于开发者来说,只关注业务函数的输入和输出,不需要关心数据如何传输过去的。


GRPC

gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。grpc只不过是一种特殊的rpc协议框架,相比常规的rpc,grpc拥有更好性能。

  • http2.0

  • protobuf

http2.0的好处在于二进制分帧、多路复用、头部压缩、服务端push。这里不在赘述,细节参考《》数据包在网络中传输来传输去的成本,关系到带宽的成本。grpc序列化方式采用protobuf,protobuf比json和xml要好很多,缺点就是没有json好阅读。在有效的带宽中,同样的数据包,protobuf可以传输更多次。且数据越小,序列化越快。protobuf是跨平台的,和语言无关。可以用proto工具生成各个语言的代码,非常方便。

  • json是文本文件,文本文件是基于字符的。

  • protobuf是二进制编码,基于值的。


这两者有什么区别,举个例子:对于123这样的数字,json编码后占3个字节,而对于二进制编码因为123可以用int8类型来表示,所以占用一个字节。在整数和浮点数编码方面,大多数情况下json占用的空间是大于protobuf的(1,2这样的大家都是1个字节)。
protobuf序列化的message大致数据结构如下:

golang随手记-grpc

我们的传参可能有filed1、field2...这些field都是类似k、v这种紧凑在一起,差别就是有的需要length,相同类型的repeated可能多个value连在一起,然后组成整个message数据流。
然后对于k也就是tag部分,它的数据结构可能是这样的:

golang随手记-grpc

  • number就是我们定义proto时,每个字段的number

  • wire_type始终占用低3位,一共可以表示8个数字,每个数字代表不同的类型

golang随手记-grpc

看个例子

golang随手记-grpc

int64的1,protobuf是如何压榨空间的:

  1. 首先低3位 000表示0,0的话用varint编码

  2. 编码后的结果是只需要8位-1字节

int64按常理64位占用8字节,这里却只要1个字节。

varint

编码过程:

  1. 获取对应的补码

  2. 从补码低位开始,每取7位,依次从高往低拼接

  3. 拼接过程,如果当前的位置后面还要填充。则当前最高位补1,否则补0。


以int32的251为例:

golang随手记-grpc

正数的补码就是他本身,取低7位,假设叫段A

golang随手记-grpc

因为前面还有1,所以接着取7位,假设叫段B,开始拼接,段A在段B前面,段A后面还有段B,所以段A的最高位补1,段B的最高位补0。所以最终为:

golang随手记-grpc

varint编码结束,原先需要4字节的存储,现在只需要2字节。每个字节的最高位不表示本身了,而是标识它的后面还有没有数据。
解码的过程也是相反的:

golang随手记-grpc

varint编码的缺点也有,比如对于int32的数据来说,每个字节都要消耗一位msb,所以最多只能表示2^28个数 对于在2^28~2^32之间的数字,还要额外的一个字节。但是往往越大的数,出现的概率越小。


zigzag

varint对于正数可以有较大的空间优化,但是负数却适得其反,以int32的-1为列:

golang随手记-grpc

负数的最高位始终是1,这将表示负数将占满整个字节。如果采用varint编码:
先取补码(符号位不变,其余各位取反,最后加1):

golang随手记-grpc

然后每取7位,加上msb:

golang随手记-grpc

最后发现一个简简单单的-1,通过varint编码还多出一个字节。

说明一下,protobuf因为支持很多语言,有的语言没有int8,int16的概念,所以protobuf没有int8和int16

zigzag的思想是映射,把负数转成正数,然后正数再通过varint编码,来减少空间。
先来看个1+(-1) = -2的问题:


1的原码 和 -1的原码相加竟然等于-2,这很明显违反常理,于是设计出了补码,1的补码和-1的补码相加等于0,计算机通过补码的方式来运算的。
来看个-1(int32)编码的过程:

  1. int32的 -1的补码 1111xxxx1111

  2. 左移一位 1111xxxx1110

  3. 右移31位 1111xxxx1111

  4. 异或运算 0000xxxx 0001

通过这种方法可以看出-1被转成了1,然后通过varint压缩,去掉高位的0,最终4字节的-1被转成了1字节的1。

zigzag核心编解码算法:

编码:(n << 1) ^ (n >> 31)解码:(((unsignedint)n) >>1) ^ -(n & 1)


以上是关于golang随手记-grpc的主要内容,如果未能解决你的问题,请参考以下文章

随手记一个漂亮的code

Mock随手记

mongodb与python随手记

安卓随手记如何筛选报表

随手记下载|随手记app下载

随手记下载|随手记app下载