Thrift/RPC学习分享
一个完整RPC调用链路
- 客户端调用client内的方法,同时传入对应参数。这个client是公司内部封装了很多实现的细节。并且有@ThriftClient注解把指定路径下的类扫描到到容器当中。
- client调用对应的client-stub,并且把封装好的参数传入
- client去对应的服务中心拉取ipList(这个过程被叫做服务发现和路由寻址
- 由负载均衡机制选取一个合适的ip(这里面可以是先一些算法,比如哈希一致、轮询、加权轮询
- 轮询:好处是实现比较简单,坏处是,性能好性能差的机器收到的请求量相同
- 加权轮询:公司采用的方法,针对性能好的机器分配更多的请求
- 一致性哈希:可以应对某些特殊的情况,比如通过一致性哈希走小流量
- 最小连接:选择压力最小的服务器
- 执行一个调用链。在这个调用链里,可以实现熔断、降级、服务鉴权等等
- 调用上下文序列化,并且通过不同RPC的协议编码(比如thrift中的编码)发送至网络通信框架(这个时候就是TCP、UDP来做事了)
- 服务端接收到对应的请求,先通过RPC协议,反序列化、也要执行调用链,最后通过对应的service处理请求,把结果写回。
熔断、降级、限流
- 熔断:A调用B,B出错,导致A受影响,这个时候就需要熔断,让A不再调用B,防止系统雪崩
- 降级:保留核心功能,让非核心功能暂时不可用。属于人为操作
- 限流:当上游调度的速度超过自身服务速度,就采用限流的策略应对
- 令牌算法:定期往令牌桶中放置令牌,上游拿到令牌就允许调用,没拿到令牌就不允许调用。应对突发情况比桶算法优秀。
- 桶算法:桶的大小恒定,出桶的速率恒定,超过桶的量就丢弃
- 固定/滑动窗口算法:定一个窗口计数器,限定时间内,这个计数器没有减到0就允许访问。
Thrift构造
- 首先是构造一个proxy代理,因为RPC的初衷就是简单。dubbo没用过,但是公司的thrift是通过生成一个client,在这个client里面封装了所有调用远端的细节的。
- 服务发现:准确来说服务发现本身并不属于RPC的范畴。公司用的是consul,其他的还有zookeeper等,公司本身使用加权轮询来选择对应的ip
- RPC的路由与负载均衡
- 接收请求–>请求校验–>路由策略–>负载均衡。
- Example:实际场景,当我们上线一个需求的时候,是不是想着灰度发布,因为想用 小流量来验证项目的正常流转。这个时候就可以通过IP路由选择来做这个事情。众所周知,server以集群的方式提供服务,在client调用server时候,会先取出一个ipList。这个时候我们针对这个ip做一个筛选就行了。比如上次一面问我,怎么做到,让一个client的请求让一个server来处理,这里就可以在路由这一层做。使用哈希一致性算法,固定打到某个ip上去。
- 负载均衡:
- 轮询
- 随机
- 加权
- 最少连接
4. 值得注意的是,通常在负载均衡之前还有一个filter层,做一些筛选,比如根据env的信息做一些筛选。但是公司好像没做这一层。
- RPC在连接这一层。通常使用TCP的keep-alive,和应用层的心跳机制来保证连接。
- 首先建立长连接
- keep-alive这一层,默认2h没有数据传输,就关闭连接。
- 心跳机制是多久没发消息直接关闭连接。
- 在客户端发送信息之前,或者在服务端调用service-stb调用server执行的时候,会执行一条调用链。
- 比如公司实现一些熔断的操作
- 一些服务鉴权的操作(之前是一个假的鉴权,现在变成一个真的鉴权)
序列化和反序列化
在网络中传输只能是二进制,因此序列化就是对象 -> 二进制的过程
thrift序列化的协议有 compact、json、binary
在公司的thrift序列化协议通常使用的是
binary序列化格式
- 消息头部
- message_type:
- oneWay:客户端消息,不期待服务器响应(非阻塞)
- call:客户端消息,期待服务器响应
- replay:服务端消息,正常响应
- exception:服务端消息,出现异常
- msg_id:消息的序列号
- message_type:
- 消息体:
- 字段类型
- 字段长度
- 字段值
- 字段序号
- 如果是定长消息就是 k-v的形式
- 如果是不定长的就是 k-l-v
RPC与HTTP
对于RPC的理解:
1. RPC的思想
2. RPC的协议
3. RPC的实现方式
- RPC的本质是一种思想,就是调用远程的方法就像调用本地的方法一样。
如果只是在聊协议这个概念 - 首先要明确什么是协议:协议就是大家约定好,用什么格式写数据,用什么格式压缩数据,按照这个协议大家实现数据的通信。而HTTP和各种各样的RCP协议本质上就是在应用层的,定义了不同消息格式的通信协议
- RPC的实现方式:在这个实现方式上我们再聊聊RPC和HTTP不同的地方。
- 首先看域名和IP地址,在HTTP上是通过DNS解析拿到对应的域名的(其实本质上DNS解析也是一种服务发现的实现方式),而在RPC的层面,通常都会通过服务发现在公司用的是Consul。
- 对于http2,采用的是二进制来序列化来做序列化,但是对于传统的http1.1是通过Json来做序列化,而RPC的序列化可能更加灵活,用protobuf、binary来做序列化相对来说说损耗更小,性能更好。
关于序列化与反序列化
首先弄明白序列化、反序列化是什么
- 比如一个java对象,你想传输是不可能直接传输一个Java对象的,计算机只认二进制。所以把一个对象转化成一个字节序列的过程叫做序列化。
- 把一个字节序列反序列化成一个对象,叫做反序列化
应用场景 - 当你需要把你的对象在网络上传递的时候
- 当你需要把你的对象保存成文本格式的时候
常见的序列化反序列化手段
- XML:文本结构的序列化手段
- JSON:文本结构的序列化手段
- Protobuf:谷歌开发的二进制序列化
- thrift binary:公司采用的二进制序列化