RPC
远程过程调用 Remote Procedure Call,客户程序通过接口调用另外一台服务器内部的标准或自定义函数,获得函数返回的数据进行处理
传输协议
OSI 通信模型中,RPC 跨越 传输层
、应用层
传输层常见协议:
- TCP, 连接是基于字节流,一种可以保证可靠数据传输的传输层协议,如网页请求资源。
- UDP, 基于报文流,一种无连接的传输层协议,它无法保证数据传输可靠性,但传输效率更高,开销更小,如视频、语言电话。
Node.js net 模块
- socket
单工通信
客户端:
1 | const net = require('net'); |
服务端:
1 | const net = require('net'); |
半双工通信
客户端:
1 | const net = require('net'); |
服务端:
1 | const net = require('net'); |
全双工通信
TCP传输协议面向流的,没有消息保护边界,所有会出现 粘包
和 拆包
解决方案:指定协议规则(包序号1-2字节 + 包长度3-6字节 + 包主体)
客户端:
1 | const net = require('net'); |
服务端:
1 | const net = require('net'); |
RPC框架内存使用
RPC 主要工作
网络收发 – 压缩解压 – 序列化反序列化
本质上一条 RPC 消息内存是离散的,都需要有自己的 buffer 类型
buffer 设计
链表每块大小怎么定义?
malloc/free
作为系统调度,比较耗费性能
一般来说内存分配交给 ptmalloc、jemalloc、tcmalloc
碎片管理
碎片管理会分很多层,大小、使用时间等都会影响这块内存回收到哪层。那么我们组织这些内存碎片的数据结构,必须满足能够互斥、快速找到适合的碎片、碎片及时回收等需求
RPC 核心
目的:调用远程方法像调用本地一样无差别
本质:屏蔽远程调用网络相关细节
调用基本流程通过动态代理实现
RPC 会给接口生成一个代理类,所以我们调用这个接口实际调用的是动态生成的代理类,由代理类来触发远程调用
动态代理: Spring(AOP)、JDK 动态代理、cglib、Dubbo(Javassist)

调用细节
序列化处理参数,需要综合考虑通用性、性能、可读性和兼容性
RPC协议:协议头 + 协议体
协议头放一些元数据,包括:魔法位、协议的版本、消息的类型、序列化方式、整体长度、头长度、扩展位等。
协议体就是放请求的数据网络传输
IO 模型:
- IO 多路复用 (
推荐
大多数RPC调用场景都是高并发) - 同步阻塞 BIO
- 同步非阻塞 NIO
- 异步非阻塞 AIO
JAVA 使用的轮子 Netty
- IO 多路复用 (
工厂级别 RPC,集群部署,需要注册中心
路由分组策略:实现分组调用、灰度发布、流量隔离等
负载均衡
异常重试
限流熔断
泛化调用
常见 RPC 框架
RMI、Dubbo、gRPC、Hessian(二进制协议)、Thrift