RPC
- 1. 基础
- 1.1 定义&特点
- 1.2 具体实现框架
- 1.3 应用场景
- 2. RPC的关键技术点&一次调用rpc流程
- 2.1 RPC流程
- 流程
- 两个网络模块如何连接的呢?
- 其它特性
- RPC优势
- 2.2 序列化技术
- 序列化方式
- PRC如何选择序列化框架
- 考虑因素
- 2.3 应用层的通信协议-http
- 2.3.1 基础概念
- 大多数RPC大多自研http,也支持合http1.1
- 什么是IO
- 边缘触发
- 水平触发
- 事件驱动 IO
- 异步 IO
- 2.3.2 操作系统的IO模型有哪些
- 同步阻塞IO
- 非阻塞式 IO
- IO多路复用
- select
- poll
- epoll
- 2.3.3 IO总结
- 2.3.4 线程模型
- 2.4 动态代理
- 其它动态代理方案
- 2.5 基于ZK注册的原理
- 基于zk注册数据的存储结构-以dubbo为例
- 2.6 容错策略之超时重试
- 什么是超时重试?
- 如何检测请求是否超时?
- 时间轮算法
- 2.7 熔断限流
- 熔断降级的概念
- 如何判断熔断
- 熔断工作合适开始?又合适结束
- 有了熔断降级为何还要限流?
- 常见的限流算法
- 3. 实现一个自己定义的RPC框架-mini版
- 3.1 PRC框架
- 3.2 Netty
- 3.3Dubbo
1. 基础
1.1 定义&特点
RPC,remote procedure call,远程过程调用,它定义了一台机器上的程序去调用另一台机器上子程序的这一行为
特点:
- 把远程实现搬到了本地,效果上远程调用和本地调用没有差别
- 使用cs模式,客户端发起请求,服务端接收请求参数后执行
- 屏蔽跨进程跨网络调用底层复杂性让我们更专注于业务逻辑
1.2 具体实现框架
- dubbo(apache alibaba java)
- motan(微博)
- tars(腾讯内部)
- grpc
- thrift
- spring cloud openfeign
1.3 应用场景
跨网络通信都可以用
2. RPC的关键技术点&一次调用rpc流程
2.1 RPC流程
流程
- 客户端调接口走到代理类,组装请求并序列化,然后协议编码并发送
- 服务端收到请求,进行协议解析以及反序列化拿到请求参数
- 服务端根据请求参数调用接口实现,然后组装响应
- 响应按同样的方式返回
两个网络模块如何连接的呢?
注册中心就是一个存数据的地方,最好可以提供监听功能。注册中心与rpc框架是分开的
常见的注册中心:zookeeper、nacos、etcd
其它特性
- 路由筛选可用的提供者
- 负载均衡:从可用的提供者种,选择具体用哪个
- 熔断限流:流量控制
- 网络处理
- 协议处理
RPC优势
- 让构建分布式应用更容易,解耦服务,容易扩展
- RPC一般使用长连接,不必每次通信都要建立连接,减少网络开销
- RPC需要有注册中心,可以动态感知服务变化并可视化
- 丰富的后台管理功能,可统一管理接口服务,对调用方来说是无感知的,统一化的操作
- 协议精简,效率更高,私密安全性高
- 具有负载均衡,熔断限流等功能
2.2 序列化技术
- 任何一种序列化框架:核心思想就是设计一种序列化协议,将对象的类型、属性类型属性值–按照固定的格式写到二进制字节流中来完成序列化,再按照固定的格式一一读出对象的类型、属性类型、属性值,通过这些信息重新创建出一个新的对象,来完成反序列化
序列化方式
- JDK原生序列化
- 轻量级文本数据交换格式-json/XML
- 可读性好,方便阅读和调试,多语言支持序列化以后的字节文件相对较大,效率相对不高,但对比XML序列化后的字节流更小,在企业运用普遍,特别是对前端和三方提供api。
- Hessian是一个动态类型,二进制,并支持跨语言的徐丽华框架
- Hessian 性能上要比JDK、JSON 序列化高效很多,并且生成的字节数也更小。有非常好的兼容性和稳定性,所以Hessian 更加适合作为 RPC框架远程通信的序列化协议
- protobuf
- Google 推出的开源序列库,它是一种轻便,高效的结构化数据存储格式,多语言支持。
- 速度快,压缩比高,体积小,序列化后体积相比JSON、Hessian 小很多课彩盘格式的扩展、升级和兼容性都不错,可以做到向后兼容
PRC如何选择序列化框架
- 选型因素
- 安全性: 首要考虑,如果序列化存在安全漏洞,那么线上的服务就很可能被入侵(JDK原生序列化存在漏洞
- 兼容性: 序列化协议在版本升级后的兼容性是否很好,是否是跨平台、跨语言等
- 通用性:能够对任意类型进行序列化和反序列化,不会出现服务接口方法加一个某种类型的参数后服务器突然不能调用
- 性能、效率:序列化与反序列化过程是 RPC调用的一个必须过程,性能和效率势必将直接关系到 RPC 框架整体的性能和效率
- 空间开销:序列化之后的二进制数据的体积大小,序列化后的字节数据体积越小,网络传输的数据量就越小,传输的数据的速度也就越快,在RPC调用中直接关系到请求响应的耗时
考虑因素
- 避免对象构造得过于复杂,属性很多,并且存在多层的嵌套
- 避免对象过于庞大:大字符串,超大数组等
- 避免使用序列化框架不支持的类型作为参数传递
- 防止对象有复杂的继承关系
2.3 应用层的通信协议-http
2.3.1 基础概念
大多数RPC大多自研http,也支持合http1.1
什么是IO
IO就是计算机内部与外部设备之间拷贝数据的过程
网络数据到来后先存储到操作系统的内核缓存区,在等待应用程序收走
边缘触发
使用边缘触发模式时,当被监控的 Socket 描述符上有可读事件发生时,服务器端只会从 epoll_wait 中苏醒一次,即使进程没有调用 read 函数从内核读取数据,也依然只苏醒一次,因此我们程序要保证一次性将内核缓冲区的数据读取完。
水平触发
使用水平触发模式时,当被监控的 Socket 上有可读事件发生时,服务器端不断地从 epoll_wait 中苏醒,直到内核缓冲区数据被 read 函数读完才结束,目的是告诉我们有数据需要读取。
事件驱动 IO
发起读请求后,等待读就绪事件通知再进行数据读取。
异步 IO
发起读请求后,等待操作系统读取完成后通知,完全将功能交给操作系统实现。
2.3.2 操作系统的IO模型有哪些
同步阻塞IO
同步非阻塞IO
IO多路复用
信号驱动IO
异步IO
同步阻塞IO
read 的第一个阶段阻塞的,这就是我们常说的阻塞式 IO,即如果read 第一个阶段等待读就绪是阻塞的,我们就称为阻塞式IO
listenfd = socket(); // 打开一个网络通信套接字
bind(listenfd); // 绑定
listen(listenfd); // 监听
while(1) {connfd = accept(listenfd); // 阻塞 等待建立连接int n = read(connfd, buf); // 阻塞 读数据