Java-63 深入浅出 分布式服务 网络通信 RPC 与 RMI 详解
点一下关注吧!!!非常感谢!!持续更新!!!
🚀 AI篇持续更新中!(长期更新)
AI炼丹日志-29 - 字节跳动 DeerFlow 深度研究框斜体样式架 私有部署 测试上手 架构研究,持续打造实用AI工具指南!📐🤖
💻 Java篇正式开启!(300篇)
目前2025年07月02日更新到:
Java-61 深入浅出 分布式服务 一致性算法 Raft 多图详解
MyBatis 已完结,Spring 已完结,Nginx已完结,Tomcat已完结,分布式服务正在更新!深入浅出助你打牢基础!
📊 大数据板块已完成多项干货更新(300篇):
包括 Hadoop、Hive、Kafka、Flink、ClickHouse、Elasticsearch 等二十余项核心组件,覆盖离线+实时数仓全栈!
大数据-278 Spark MLib - 基础介绍 机器学习算法 梯度提升树 GBDT案例 详解
分布式架构网络通信
在分布式系统设计中,网络通信是最基础也是最重要的组成部分。在Java生态系统中,有多种技术可以实现远程服务通信,每种技术都有其特定的应用场景和实现原理:
- RMI (Remote Method Invocation):Java原生的远程调用框架
- Hessian:基于HTTP的轻量级RPC框架
- SOAP:基于XML的Web服务协议
- ESB (Enterprise Service Bus):企业服务总线
- JMS (Java Message Service):面向消息的中间件API
这些技术虽然实现方式不同,但都建立在相同的底层网络通信原理之上。
基本原理
网络通信基础
从计算机系统底层来看,网络通信的本质是将数据从一台计算机传输到另一台计算机。这个过程主要涉及两个关键组件:
-
传输协议:
- TCP:面向连接的可靠传输协议,提供数据顺序保证和重传机制
- 三次握手建立连接
- 滑动窗口控制流量
- 四次挥手断开连接
- UDP:无连接的不可靠传输协议,提供较低的延迟
- 适用于实时性要求高但允许少量丢包的场景
- 常用于视频会议、在线游戏等
- TCP:面向连接的可靠传输协议,提供数据顺序保证和重传机制
-
网络IO模型:
- BIO (Blocking IO):
- 同步阻塞模型
- 每个连接需要一个独立线程处理
- 适用于连接数较少的场景
- NIO (Non-blocking IO):
- 同步非阻塞模型
- 基于Selector实现多路复用
- 适合高并发场景
- AIO (Asynchronous IO):
- 异步非阻塞模型
- 基于回调机制
- 性能最高但实现复杂
- BIO (Blocking IO):
高级抽象
在实际应用中,开发者很少直接操作底层协议,而是使用更高层次的抽象:
- HTTP/HTTPS协议
- WebSocket协议
- 各种RPC框架
这些高级协议和框架封装了底层细节,提供了更友好的开发接口。
什么是RPC
RPC (Remote Procedure Call) 是一种分布式系统间的通信范式,其核心目标是让远程服务调用像本地调用一样简单。
RPC工作原理示例
假设有以下场景:
- 服务器A运行订单服务
- 服务器B运行支付服务
- 订单服务需要调用支付服务的支付接口
传统方式需要:
- 建立网络连接
- 序列化请求参数
- 发送请求
- 接收响应
- 反序列化结果
而使用RPC框架后,开发者只需:
PaymentService payment = RPCClient.getProxy(PaymentService.class);
PaymentResult result = payment.pay(orderId, amount);
RPC核心特征
- 透明性:调用远程方法像调用本地方法一样
- 跨语言:许多RPC框架支持多语言互操作
- 高性能:通过协议优化减少通信开销
- 可扩展:支持服务注册与发现等分布式特性
RPC架构
一个完整的RPC架构包含以下组件及其详细工作流程:
1. Client (客户端)
- 服务消费者
- 发起远程调用
- 典型行为:
- 获取服务代理
- 构造调用请求
- 等待响应返回
2. Client Stub (客户端存根)
- 代理对象
- 核心职责:
- 服务发现:从注册中心获取服务地址
- 协议编码:将方法调用转换为网络消息
- 方法名序列化
- 参数序列化(JSON, Protobuf等)
- 网络传输:通过通信框架发送请求
- 结果解码:将响应数据反序列化为对象
3. Server (服务端)
- 服务提供者
- 包含实际业务实现
- 典型行为:
- 注册服务到注册中心
- 监听处理请求
- 执行本地方法
4. Server Stub (服务端存根)
- 请求处理器
- 核心职责:
- 网络监听:接收客户端请求
- 协议解码:解析请求数据
- 识别目标方法
- 反序列化参数
- 方法调用:反射调用本地实现
- 结果编码:序列化返回结果
Stub的工作流程示例
Client -> ClientStub: 调用远程方法
ClientStub -> Network: 序列化请求
Network -> ServerStub: 传输请求数据
ServerStub -> Server: 调用本地方法
Server -> ServerStub: 返回结果
ServerStub -> Network: 序列化响应
Network -> ClientStub: 传输响应数据
ClientStub -> Client: 返回调用结果
常见优化技术
- 连接池:复用TCP连接减少握手开销
- 异步调用:Future/Promise模式
- 负载均衡:多服务提供者情况下的流量分配
- 容错处理:超时重试、熔断降级等机制
RPC调用过程
● 客户端(Client)发起服务调用
- 客户端通过本地接口调用的方式发起远程服务请求
- 调用方式与调用本地方法类似,对开发者透明
- 示例:Java中的RMI调用或Dubbo服务调用
● 客户端存根(Client Stub)处理请求
- 接收客户端调用请求后,解析方法签名和参数
- 将调用信息序列化为二进制数据:
- 使用协议如Protocol Buffers、JSON或Java原生序列化
- 包含方法名、参数类型、参数值等信息
- 构建网络传输的消息包:
- 添加消息头(如消息长度、版本号)
- 封装序列化后的数据为传输格式
● 网络传输过程
- 客户端通过Socket建立与服务端的TCP连接
- 使用NIO或BIO模式发送消息数据包
- 可能经过负载均衡器或服务注册中心路由
- 典型应用场景:跨机房调用或微服务间通信
● 服务端存根(Server Stub)处理
- 接收网络字节流并进行完整性校验
- 反序列化过程:
- 读取消息头解析协议版本
- 根据协议定义解码方法调用信息
- 重建方法参数对象
- 完整性检查:校验方法签名和参数合法性
● 服务调用执行
- 服务端存根通过反射机制定位本地服务实现
- 参数类型转换和适配(如DTO转换)
- 调用实际业务逻辑处理
- 记录调用日志和监控指标
● 结果返回处理
- 服务端将执行结果序列化:
- 处理正常返回值或异常信息
- 考虑压缩优化网络传输
- 构建响应消息包:
- 包含响应状态码
- 附加调用耗时等元数据
● 客户端结果处理
- 接收网络响应并校验完整性
- 反序列化响应数据:
- 解析响应状态
- 重建返回结果对象
- 异常处理机制:
- 网络超时重试
- 服务降级策略
- 最终将结果返回给调用方
整个过程可能涉及:
- 服务治理功能(熔断、限流)
- 加密传输(TLS/SSL)
- 异步回调机制
- 跨语言支持(通过通用协议)
- 性能优化(连接池、批处理)
PRC的主要目标是把 除第一步和最后一步的结果组转起来。
注意:无论是何种类型的数据,最终都需要转换为二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流在恢复为对象
在Java的RPC框架比较多,常见的:
● Hessian
● gRPC
● Thrift
● HSF(High Speed Service Framework)
● Dubbo
● 等等
RMI (Remote Method Invocation)
基本介绍
Java RMI(Remote Method Invocation,远程方法调用)是 Java 原生支持的分布式远程调用框架,它采用 JRMP(Java Remote Messaging Protocol)作为通信协议,是纯 Java 版本的分布式远程调用解决方案。RMI 主要用于不同 Java 虚拟机(JVM)之间的通信,这些 JVM 可以运行在不同的主机上,也可以运行在同一台主机上。通过 RMI,一个 JVM 中的对象可以调用另一个 JVM 中对象的方法,就像调用本地对象的方法一样简单。
工作原理
客户端组件
-
存根(Stub)
- 远程对象在客户端的代理,负责隐藏网络通信细节
- 实现了与远程对象相同的接口
- 当客户端调用方法时,Stub 会将调用请求序列化并通过网络发送到服务器端
-
远程引用层(Remote Reference Layer)
- 解析并执行远程引用协议
- 处理远程对象的引用语义(如单一引用或复制引用)
- 管理远程对象的生命周期
-
传输层(Transport)
- 负责建立和管理网络连接
- 发送方法调用请求
- 传递远程方法参数(通过对象序列化)
- 接收并返回远程方法执行结果
- 使用 TCP/IP 协议进行通信
服务器端组件
-
骨架(Skeleton)
- 接收客户端通过 Stub 发送的请求
- 反序列化方法参数
- 调用服务器端的实际对象方法
- 将执行结果序列化并返回给客户端
- 在新版 RMI 中,Skeleton 已被动态代理机制取代
-
远程引用层(Remote Reference Layer)
- 处理远程引用语义
- 将请求转发给适当的 Skeleton 或目标对象
- 管理远程对象的注册和查找
-
传输层(Transport)
- 监听客户端的入站连接(默认端口 1099)
- 接收客户端请求
- 将请求转发给远程引用层
- 管理连接池和线程池
典型应用场景
- 分布式计算:将计算任务分配到多台机器上执行
- 远程服务调用:如访问远程数据库服务或文件服务
- 企业级应用集成:集成不同系统之间的功能
- 集群管理:管理分布在多台服务器上的服务实例
工作流程示例
- 服务器端创建远程对象实现
- 使用 RMI 注册表(rmiregistry)注册远程对象
- 客户端从注册表获取远程对象引用(通过名称查找)
- 客户端调用远程对象方法
- Stub 序列化参数并发送请求
- 服务器端 Skeleton 接收请求并调用实际方法
- 方法执行结果返回给客户端
- 客户端获取返回值继续执行
优缺点
优点:
- 简单易用,与本地方法调用语法相似
- 完全 Java 实现,跨平台性好
- 支持对象序列化和动态类加载
- 内置安全机制
缺点:
- 仅支持 Java 语言间的通信
- 性能不如一些二进制协议高效
- 防火墙穿透能力较差
- 在复杂网络环境下可能遇到连接问题
注册表:
以URL形式注册远程对象,并向客户端回复远程对象的引用。
远程调用(RPC)的详细执行过程
远程调用的完整流程
-
服务发现阶段
- 客户端首先通过远程服务器的注册中心(如Zookeeper、Nacos等)查询服务注册信息
- 注册中心返回可用的服务实例列表和元数据信息
- 客户端根据负载均衡策略选择一个合适的服务实例
-
代理调用阶段
- 客户端持有的桩对象(Stub)实现了与远程服务完全一致的接口
- 当客户端调用桩对象的方法时(如
userService.getUser(id)
),桩对象会:- 序列化方法名和参数(使用JSON、Protobuf等格式)
- 生成唯一的请求ID用于跟踪调用
- 添加必要的元数据(如超时设置、认证信息)
-
网络传输阶段
- 远程引用层将本地调用转换为网络请求:
- 确定目标服务器的地址和端口
- 建立TCP连接(或复用连接池中的连接)
- 将序列化后的请求数据通过传输层发送
- 典型实现方式:
- HTTP协议(如RESTful API)
- 二进制协议(如gRPC的HTTP/2)
- 自定义协议(如Dubbo的TCP协议)
- 远程引用层将本地调用转换为网络请求:
-
服务器处理阶段
- 传输层监听端口(如8080),接收到请求后:
- 解析协议头信息
- 验证请求合法性(如认证、限流)
- 将请求转发给上层的远程引用层
- 远程引用层执行:
- 将网络请求转换为本地对象引用
- 反序列化方法名和参数
- 查找对应的服务实例和方法
- 传输层监听端口(如8080),接收到请求后:
-
实际调用阶段
- 骨架(Skeleton)组件:
- 验证参数类型和数量
- 准备调用上下文(如事务、跟踪ID)
- 通过反射机制调用实际的服务实现
- 服务实现类执行真正的业务逻辑:
- 访问数据库
- 调用其他服务
- 执行计算任务
- 骨架(Skeleton)组件:
结果返回的详细过程
-
服务器端结果处理
- 业务方法执行完成后:
- 返回值被骨架组件捕获
- 异常情况会被转换为特定的错误码
- 序列化过程:
- 使用与请求相同的序列化协议
- 可能包含附加信息(如执行耗时、扩展字段)
- 业务方法执行完成后:
-
网络传输返回
- 传输层将响应数据分包发送:
- 大响应可能被拆分为多个TCP包
- 需要处理网络抖动和重传
- 常见的优化措施:
- 数据压缩(如Gzip)
- 批量响应(如多个请求合并返回)
- 传输层将响应数据分包发送:
-
客户端结果处理
- 客户端传输层:
- 接收数据包并重组完整响应
- 处理网络超时和重试逻辑
- 桩对象反序列化:
- 将二进制数据转换为Java对象
- 处理异常和错误码
- 验证响应完整性(如校验和)
- 客户端传输层:
-
最终结果交付
- 桩对象将反序列化的结果返回给调用方:
- 同步调用:直接返回结果对象
- 异步调用:返回Future或回调处理
- 资源清理:
- 关闭网络连接
- 释放缓冲资源
- 记录调用指标(用于监控)
- 桩对象将反序列化的结果返回给调用方:
典型应用场景示例
-
微服务架构中的服务调用
- 订单服务调用库存服务的扣减接口
- 用户服务查询权限服务的角色信息
-
分布式计算任务
- MapReduce任务分发
- 分布式机器学习参数同步
-
跨语言系统集成
- Java服务调用Python的算法服务
- Go语言编写的支付服务被NodeJS电商系统调用
-
云原生应用
- Kubernetes Pod之间的服务发现和调用
- Serverless函数间的相互调用