1. 客户端发起调用
1.1 定义服务接口
- 客户端和服务端约定统一的接口(如 Java Interface),接口定义了方法名、参数和返回值类型。
1.2 客户端代理(Stub)
- 动态代理:客户端通过动态代理(如 JDK Proxy、CGLib)生成接口的代理对象。
- 封装请求:调用代理对象的方法时,代理会将方法名、参数等信息封装为请求对象(Request)。
1.3 服务发现与负载均衡
- 客户端通过服务注册中心(如 ZooKeeper、Nacos、Consul)获取服务端的地址列表。
- 根据负载均衡策略(如轮询、随机、一致性哈希)选择一个目标服务节点。
2. 序列化与编码
2.1 序列化
请求对象 → 二进制数据:将请求对象(方法名、参数等)转换为字节流,以便网络传输。
常见序列化协议
- JSON/XML:可读性好,性能较低。
- Protobuf(Google)、Thrift(Facebook)、Hessian:二进制协议,性能高。
- Avro、Kryo:支持 Schema 定义,适合复杂数据结构。
2.2 协议编码
将序列化后的数据按通信协议封装为网络报文(如添加 Header 描述数据长度、版本、校验码等)。
示例协议格式
plaintextplaintext 复制代码| Header(Magic Number + 版本号 + 数据长度) | Body(序列化后的请求数据) |
3. 网络传输(Socket 通信)
3.1 建立连接
- 客户端通过 TCP 或 HTTP/2(如 gRPC)与服务端建立 Socket 连接。
- 连接池管理:为减少频繁建立连接的开销,通常使用连接池复用 TCP 连接。
3.2 发送请求
客户端通过 Socket 将编码后的二进制数据发送到服务端。
可靠性保障
- 超时重试:设置超时时间,超时后重试或降级处理。
- ACK 确认:部分协议要求服务端返回 ACK 确认数据接收。
4. 服务端处理请求
4.1 接收请求
服务端通过 Socket Server 监听端口,接收客户端请求。
IO 模型
- BIO:阻塞式,每个连接占用一个线程(性能低)。
- NIO:非阻塞,基于事件驱动(如 Netty 框架)。
- AIO:异步 IO(较少使用)。
4.2 解码与反序列化
- 协议解析:从报文中提取 Header 和 Body,验证数据完整性(如校验码)。
- 反序列化:将 Body 的二进制数据还原为服务端可理解的请求对象。
4.3 路由与方法调用
根据请求中的接口名和方法名,通过反射或预生成代理调用服务端的具体实现类。
示例代码
javajava 复制代码Method method = service.getClass().getMethod(request.getMethodName(), paramTypes); Object result = method.invoke(service, request.getArgs());
4.4 执行业务逻辑
- 服务端执行实际业务逻辑(如数据库操作、计算等)。
5. 返回响应
5.1 序列化响应
- 将执行结果(或异常信息)序列化为二进制数据。
5.2 编码与发送
- 按协议封装响应数据,通过 Socket 返回给客户端。
6. 客户端处理响应
6.1 接收响应
- 客户端 Socket 接收服务端返回的二进制数据。
6.2 反序列化与解码
- 解析协议,反序列化响应数据为客户端可理解的响应对象。
6.3 返回结果
- 动态代理将响应对象转换为接口定义的返回值类型,返回给客户端调用方。
7. 关键问题与优化
7.1 序列化优化
- 压缩算法:对大数据使用 Snappy、GZIP 压缩。
- 兼容性:通过版本号或 Schema 演进(如 Protobuf 的字段可选性)支持接口升级。
7.2 网络优化
- 长连接:复用 TCP 连接减少握手开销。
- 零拷贝:使用 Netty 的
ByteBuf
或 Linux 的sendfile
减少内存复制。
7.3 容错与高可用
- 熔断降级:通过 Hystrix 或 Sentinel 防止服务雪崩。
- 重试机制:对可重试的异常(如网络超时)自动重试。
7.4 异步调用
- 客户端通过 Future 或 CompletableFuture 实现异步非阻塞调用。
完整流程图
客户端调用 → 动态代理封装请求 → 序列化 → 协议编码 → Socket 发送 → 网络传输 →
服务端接收 → 协议解码 → 反序列化 → 反射调用 → 执行业务 → 序列化响应 →
协议编码 → Socket 返回 → 客户端接收 → 反序列化 → 返回结果
示例:基于 Netty 的 RPC 框架
- 客户端:使用 Netty 的
Channel
发送请求,通过Future
异步等待响应。 - 服务端:基于 Netty 的
EventLoopGroup
处理请求,利用线程池执行业务逻辑。 - 协议设计:自定义协议头(如
length + requestId + serializationType
)。
常见 RPC 框架
- gRPC:基于 HTTP/2 和 Protobuf,支持多语言。
- Dubbo:阿里开源的 Java RPC 框架,集成服务治理功能。
- Thrift:Facebook 开源,支持多种序列化协议和传输层。
- Spring Cloud Feign:基于 HTTP 的声明式 RPC 客户端。
总结
RPC 的核心是通过网络透明地调用远程服务,其实现依赖序列化、网络通信、动态代理和服务治理等关键技术。优化方向包括性能(序列化效率、网络模型)、可靠性(重试、熔断)和扩展性(服务发现、负载均衡)。