grpc

247 阅读5分钟

这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战

什么是rpc

  • rpc远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
  • 对应rpc的是本地调用过程,函数调用是最常见的本地过程调用
  • 将本地过程调用变成远程过程调用会面临各种问题

本地过程调用

  • 将1和2压入add函数的栈
  • 进入add函数,从栈中取出1和2分别赋值给a和b
  • 执行a+b将结果赋值给局部的total并压栈
  • 将栈中的值取出来赋值给全局的total

远程过程面临的问题

  • 原本的本地函数放到另一个服务器去运行,但是引入了很多新问题
  • call的id映射
  • 序列化和反序列化
  • 网络传输

在远程调用时,我们需要执行的函数体是在远程的机器上的,也就是说,add是在另一个进程中执行的。这就带来了几个新问题:

  1. Call ID映射。我们怎么告诉远程机器我们要调用add,而不是sub或者Foo呢?在本地调用中,函数体是直接通过函数指针来指定的,我们调用add,编译器就自动帮我们调用它相应的函数指针。但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。客户端在做远程过程调用时,必须附上这个ID。然后我们还需要在客户端和服务端分别维护一个 {函数 <--> Call ID} 的对应表。两者的表不一定需要完全相同,但相同的函数对应的Call ID必须相同。当客户端需要进行远程调用时,它就查一下这个表,找出相应的Call ID,然后把它传给服务端,服务端也通过查表,来确定客户端需要调用的函数,然后执行相应函数的代码。
  2. 序列化和反序列化。客户端怎么把参数值传给远程的函数呢?在本地调用中,我们只需要把参数压到栈里,然后让函数自己去栈里读就行。但是在远程过程调用时,客户端跟服务端是不同的进程,不能通过内存来传递参数。甚至有时候客户端和服务端使用的都不是同一种语言(比如服务端用C++,客户端用Java或者Python)。这时候就需要客户端把参数先转成一个字节流,传给服务端后,再把字节流转成自己能读取的格式。这个过程叫序列化和反序列化。同理,从服务端返回的值也需要序列化反序列化的过程。
  3. 网络传输。远程调用往往用在网络上,客户端和服务端是通过网络连接的。所有的数据都需要通过网络传输,因此就需要有一个网络传输层。网络传输层需要把Call ID和序列化后的参数字节流传给服务端,然后再把序列化后的调用结果传回客户端。只要能完成这两者的,都可以作为传输层使用。因此,它所使用的协议其实是不限的,能完成传输就行。尽管大部分RPC框架都使用TCP协议,但其实UDP也可以,而gRPC干脆就用了HTTP2。Java的Netty也属于这层的东西。

client端解决的问题:

  1. 将这个调用映射为Call ID。这里假设用最简单的字符串当Call ID的方法
  2. 将Call ID,a和b序列化。可以直接将它们的值以二进制形式打包
  3. 把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
  4. 等待服务器返回结果
  5. 如果服务器调用成功,那么就将结果反序列化,并赋给total

server端解决的问题:

  1. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用dict完成
  2. 等待请求,包括多线程的并发处理能力
  3. 得到一个请求后,将其数据包反序列化,得到Call ID
  4. 通过在call_id_map中查找,得到相应的函数指针
  5. 将a和rb反序列化后,在本地调用add函数,得到结果
  6. 将结果序列化后通过网络返回给Client

技术选型要考虑的点:

  1. 超时机制
  2. 限流,处于长期可用状态-高可用
  3. 解耦
  4. 负载均衡,微服务 - 分布式应用的一种具体的体现
  5. 序列化和反序列化数据压缩是否高效 json这种数据格式已经非常的简单了 1.这个序列化协议能将数据的压缩变得更小 2. 这个序列化和反序列化的速度够快 #json.dumps() json.loads()
  6. 这个rpc框架是否支持多语言 生态很好 (实际上真正的开发过程中,除了上面的基本功能以外还需要更多的细节,网络错误、流量控制、超时和重试等)

rpc需要解决的问题

  • id映射
  • 传输协议,tcp/http
  • 数据的编码和解码 http/json/xml/其他
  • 如何解决高并发问题
  • 负载均衡问题
  • 集群的问题

选择哪一种rpc解决方案

  • 生态
  • 支持的语言(多语言/单语言)

grpc和protobuf

gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.

protobuf java中的dubbo dubbo/rmi/hessian messagepack 如果你懂了协议完全有能力自己去实现一个协议

  • 习惯用 Json、XML 数据存储格式的你们,相信大多都没听过Protocol Buffer
  • Protocol Buffer 其实 是 Google出品的一种轻量 & 高效的结构化数据存储格式,性能比 Json、XML 真的强!太!多!
  • protobuf经历了protobuf2和protobuf3,pb3比pb2简化了很多,目前主流的版本是pb3