携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
目标 想获得一次 创作先锋奖 希望同志们 支持下
序
先说目标
用两篇文档 和大家一起 思考/整理 如果我们自己写rpc框架需要考虑什么/怎么实现
会讲解的内容
-
协议 必须 可扩展且向后兼容 怎么实现呢?
-
调用过程中超时了怎么处理业务?
-
什么场景下最适合使用 RPC?
-
什么时候才需要考虑开启压缩?
-
接口是契约,需要接口设计者做向下兼容
-
流控,错误处理
-
最难的问题:分布式事务处理
-
RPC 里面,我们是怎么实现请求跟响应关联的呢?
-
既然基于TCP优于HTTP,gRPC为什么选择基于HTTP2?
grpc基于http2,更容易跨语言支持。
RPC 使用过程中需要注意哪些问题?
- 下游服务的服务能力,避免因为你的调用把别人给调挂了,要事前协商好qps等,做好限流
- 调用服务异常时,要考虑降级、重试等措施
- 核心的服务不能强依赖非核心的服务,避免核心服务因为非核心服务异常而不可用
为什么写这个系列呢?
今天早上刚起床看到一个公众号 推送的 《RPC 实战与核心原理》 , 突然感觉 "哎 我好像还真没整理过rpc的知识,搞,我先看看需要多长时间,不能影响我睡觉啊。。。。嗯 一天时间够了 搞起"
名词解释
- RPC(Remote Procedure Call,远程过程调用)
- HTTP(HyperText Transfer Protocol 超文本传输协议)
RPC 主要是基于 TCP/IP 协议的,而 HTTP 服务主要是基于 HTTP 协议的,我们都知道 HTTP 协议是在传输层协议 TCP 之上的。
RPC 服务
RPC 架构里的四个核心的组件:
- 客户端(Client),服务的调用方。
- 服务端(Server),真正的服务提供者。
- 客户端存根(Client Stub),存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
- 服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。
支持同步调用和异步调用
HTTP 服务
即:RESTful 风格的服务接口。优点就是简单、直接、开发方便。利用现成的 http 协议进行传输。
接口可能返回一个 JSON 字符串或者是 XML 文档。然后客户端再去处理这个返回的信息,从而可以比较快速地进行开发。
自己思考下 rpc 需要实现什么功能
简单想 不就是把服务A 的body 序列化 通过协议 传输给 服务B ,服务B处理完之后 ,再进行返回给服务A
好,那我们就从这个最简单的需求 实现
| 比较 | http | rpc |
|---|---|---|
| 传输协议 | 基于HTTP协议 | 可以基于TCP协议,也可以基于HTTP协议 |
| 传输效率 | 请求中会包含很多无用的内容 | 减少报文体积,提高传输效率 |
| 性能消耗 | 大部分是通过json实现的,字节大小和序列化耗时都比thrift要更消耗性能 | 可以基于thrift实现高效的二进制传输 |
| 性能消耗 | 需要配置Nginx,HAProxy实现 feign也可以 | 自带了负载均衡策略 |
RPC主要用于公司内部服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器调用,APP接口调用,第三方接口调用等等。
RPC和HTTP都可以用于实现远程过程调用,如何选择 从速度上看,RPC比HTTP更快,虽然底层都是TCP,但是http协议的信息往往比较臃肿,不过可以采用gzip压缩 从难度上看,RPC实现较为复杂,http相对简单 从灵活性上看,HTTP更胜一筹,因为它不关心实现细节,跨平台,跨语言
RPC 的作用就是体现在这样两个方面:
- 屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
- 隐藏底层网络通信的复杂性,让我们更专注于业务逻辑。
你是不是以为到这就结束了?
并没有 上面这个 百度就可以搜索到,但是 http 和rpc 到底性能差多少,多余的body 能有多大,都是什么呢? 对不起 这个写在后面的文章中 哈哈 写作中
RPC的通信流程
- Bit Offset——标识协议的其实位置
- 魔术位——标识是什么协议
- 整体长度——标识整个协议有多长,减去协议头长度就是协议体长度
- 头长度——标识协议头的长度,因为头是可扩展的,所以具体长度不固定,需要标识一下
- 协议版本——标识当前协议的版本,用于协议兼容性控制
- 消息类型——标识消息的类型,对于文本的需要,这里也需要嘛?协议类型可能是对象?可能是XML文件?可能是JSON码?正常来讲应该都是对象才对,让用于反序列化,猜测是为了扩展预留的
- 序列化方式——用于消息的序列化和反序列化
- 消息ID——用于表示请求和响应的关系
- 协议头扩展字段——用于扩展协议头,是协议具有扩展性,更加的灵活可控
- 协议体——协议的内容,一堆堆的二进制数据,双方沟通的东西
- 协议头——规定信息转换的规则
- 协议体——信息真正的内容,由于在传输层对人不友好对应用程序也不友好需要转换一下
RPC 框架中如何去选择序列化协议,我们有这样几个很重要的参考因素,优先级从高到低
- 安全性
- 通用性
- 兼容性
- 性能
- 效率
- 空间开销
序列化
序列化 选型
| 标题 | JDK 原生序列化 | JSON | Hessian | Protobuf |
|---|---|---|---|---|
| 问题 | JSON 进行序列化的额外空间开销比较大,对于大数据量服务这意味着需要巨大的内存和磁盘开销; | Linked 系列,LinkedHashMap、LinkedHashSet 等,但是可以通过扩展 CollectionDeserializer 类修复;Locale 类,可以通过扩展 ContextSerializerFactory 类修复;Byte/Short 反序列化的时候变成 Integer | 对于具有反射和动态能力的语言来说,用起来很费劲 | |
| 优点 | 方便理解 | 更加高效,生成的字节数更小,有非常好的兼容性和稳定性 | 序列化后体积相比 JSON、Hessian 小很多;IDL 能清晰地描述语义,所以足以帮助并保证应用程序之间的类型不会丢失,无需类似 XML 解析器;序列化反序列化速度很快,不需要通过反射获取类型;消息格式升级和兼容性不错,可以做到向后兼容。 | |
| 应用场景 | 数据量较小可以使用 | 大部分情况使用这种 |
JDk 原生序列化
下节更新内容
- 网络IO模型
- 动态代理
- rpc demo (之后的东西都在这个demo 基础上添加,代码放到码云上)
- 单机性能怎么 得到最大的提升
之后还愿的 (新开小节 说这个)
- http rpc 性能 等参数的 数据比较