rpc叫做远程过程调用,是指一台机器上的服务通过通信协议调用网络中另一台机器上的程序,并拿到结果。
1、基本流程
基本流程为:
- 客户端程序通过Client Stub调度rpc函数
- Client Stub将调用方法、参数按照通信协议序列化成网络二进制数据;然后找到服务端实例的地址,将消息发送给这个实例;
- 服务端按照通信协议进行反序列化解码,调用本地服务,;
- 服务端将计算结果返回Service Stub,然后Stub按照通信协议序列化发送给Client Stub;
- Client Stub进行反序列化,将结果返回给客户端程序。
2、RPC基本组件
(1) 通信协议
通信协议有json/xml/IDL/Protobuf等,Protobuf相较于其他协议:压缩率高,数据包更小,节约网络带宽;序列化/反序列化时间更短;兼容性、扩展性好。
protobuf 序列化原理:参考
对于int32、int64、bool、float、double、enum使用T-V存储方式,使用Variant、Zigzag对值进行压缩编码;对于string、byte使用T-L-V存储方式,T表示tag,数据类型号+标识号,L表示字段长度,V表示字段值,这样不需要分割符就可以将字段值排在一起,排列比较紧凑。使用建议:
- 尽量用optional或repeated类型,字段在没有赋值时不会进行编码;
- repeated字段尽量用packed=true就是,这样会使用T-L-V-V-V存储方式,更节省空间
- 会出现负数值额字段尽量用sint32、sint64表示,sint32表示负数时会休闲用Varian编码再用Zigzag编码,压缩效率比较高;
- 扩展性:不要修改原有字段的标识号,新增字段使用累加的标识号;新增字段使用optional或者repeated类型;尽量新增字段不子啊原来字段上修改变量名;如果要修改字段类型,注意字段类型的兼容性,int32/int64/uint32/bool,sint32/sint64,string/bytes是兼容的。
反序列化(反射)过程:
待补充
(2)服务注册与发现
分布式环境下,服务一般以集群的方式提供服务,因此Client Stub发送协议数据时要先看到集群下的服务ip列表,然后再通过负载均衡算法确定目的服务端实例。
服务注册:服务提供方将对外暴露的接口发布到注册中心内,注册中心为了检测服务的有效状态,一般会建立双向心跳机制。
服务订阅:服务调用方去注册中心查找并订阅服务提供方的 IP,并缓存到本地用于后续调用。
可以通过zookeeper、redis、etcd等实现。服务节点数据注册在zk中,zk通过心跳监控服务节点的状态,zk集群下每个节点发生更新操作都会通知其他节点进行更新,属于强一致性。当服务节点发生变化时zk就会通知给订阅的客户端。
(3)路由与负载均衡
负载均衡策略一般有轮询、随机打散(traceid)、权重、最小连接数方式(根据服务端阻塞队列长度)。也可以自定义负载均衡策略,根据服务节点CPU、内存占用率;节点的成功率、拒绝率等按权重进行打分;
(4)熔断限流
平滑限流的滑动窗口、漏斗算法以及令牌桶算法等等。
(5)异步
rpc有同步、异步两种工作方式。异步方式下客户端注册回调函数,服务端rpc执行完返回后回调函数。服务端使用多线程reactor模式,使用epoll监听网络IO,然后将请求放入阻塞队列,线程池并发处理。