Skip to content

shaoyou520/java-rpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java-RPC 框架中文文档

一、项目概述

Java-RPC 是一个轻量级的分布式远程过程调用(RPC)框架,使用 Java 实现。它允许客户端像调用本地方法一样调用远端服务,框架自动处理网络通信、序列化、服务发现和负载均衡等底层细节。

技术栈

组件 技术 版本
构建工具 Maven 多模块 -
父框架 Spring Boot 3.3.2
网络通信 Netty 4.1.112.Final
服务注册中心 ZooKeeper + Curator Curator 5.7.0
序列化 Fastjson + Gson Fastjson 1.2.67, Gson 2.11.0
语言 Java 22
工具库 Lombok 1.18.34

二、模块结构

项目采用 Maven 多模块结构,共包含四个子模块:

java-rpc/
├── pom.xml                  # 父 POM
├── rpc-framework/           # 核心框架模块(接口定义 + 所有实现)
├── server-inter/            # 共享服务契约模块(服务接口 + 实体类)
├── client/                  # 客户端应用入口
└── server/                  # 服务端应用入口
模块 职责
rpc-framework RPC 核心基础设施:传输层实现、序列化、服务发现、负载均衡、动态代理等
server-inter 服务接口定义和数据实体,客户端和服务端共同依赖,确保类型安全
client 客户端应用入口,创建 ClientProxy 并通过代理发起 RPC 调用
server 服务端应用入口,注册服务实现并启动监听

三、整体架构

RPC 调用流程

  1. 动态代理生成ClientProxy 使用 JDK 动态代理(java.lang.reflect.Proxy),拦截接口方法调用并封装为 RPCRequest
  2. 服务发现:通过 ZkServiceRegister 从 ZooKeeper 中获取服务地址,支持负载均衡
  3. 网络传输:使用 Netty 异步 I/O 或原生 Socket 阻塞 I/O 传输数据
  4. 反射调用:服务端通过反射执行真实的服务方法并返回结果

四、核心组件详解

4.1 数据结构

RPCRequest — 请求对象

封装一次远程调用所需的全部信息:

字段 类型 说明
interfaceName String 服务接口全限定名
methodName String 方法名
params Object[] 方法参数列表
paramsTypes Class<?>[] 参数类型列表

RPCResponse — 响应对象

封装服务端返回的结果:

字段 类型 说明
code int 状态码(200=成功,500=失败)
message String 错误信息
data Object 返回数据
dataType Class<?> 数据类型(用于 JSON 反序列化时还原类型)

4.2 客户端组件

RPCClient 接口

所有客户端传输实现的顶层接口,定义了 sendRequest 方法。

ClientProxy — 动态代理

使用 JDK 动态代理机制,将接口方法调用自动转化为 RPC 请求:

  • invoke() 方法拦截所有代理对象的方法调用,构建 RPCRequest 并通过 RPCClient 发送
  • getProxy() 方法为指定接口生成代理实例

NettyRPCClient — Netty 异步客户端

基于 Netty NIO 的高性能客户端实现:

  • 使用 Bootstrap 建立连接
  • 通过 AttributeKey 在 Channel 上存取响应结果(当前为同步阻塞等待)
  • 每次请求从 ZooKeeper 发现服务地址

SimpleRPCClient — 原生 Socket 客户端

基于 Java 原生 Socket 的阻塞式客户端,使用 ObjectInputStream/ObjectOutputStream 进行 Java 原生序列化通信。

4.3 服务端组件

RPCServer 接口

所有服务端传输实现的顶层接口,定义了 start(int port)stop() 方法。

ServiceProvider — 服务提供者

本地服务注册表,管理 接口名 → 实现实例 的映射,并在 ZooKeeper 上注册服务地址:

  • provideServiceInterface() — 遍历实现类的所有接口,将接口名映射到实现实例,并在 ZooKeeper 注册
  • getService() — 根据接口名获取实现实例

NettyRPCServer — Netty 异步服务端

基于 Netty 的事件驱动服务端,使用 Boss/Worker 线程组模型。

ThreadPoolRPCRPCServer — 线程池版服务端

基于 Java 原生 Socket + 线程池的服务端实现,每个连接分配一个 WorkThread 处理。

4.4 序列化

Serializer 接口

定义序列化/反序列化规范,支持通过 getSerializerByCode() 工厂方法获取实例:

编号 序列化方式 实现类
0 Java 原生序列化 ObjectSerializer
1 JSON 序列化 JsonSerializer

JsonSerializer — JSON 序列化器

使用 Fastjson 进行序列化,反序列化时根据消息类型(Request/Response)进行不同的处理,通过 dataType 字段保留类型信息以解决 JSON 类型丢失问题。

ObjectSerializer — Java 原生序列化器

使用 ObjectOutputStream/ObjectInputStream 实现,序列化字节中自带类型信息。

4.5 自定义消息协议

采用长度前缀 + 消息头的帧协议,解决 TCP 粘包/拆包问题:

+----------------+------------------+----------------+------------------+
| 消息类型 (2字节) | 序列化类型 (2字节) | 数据长度 (4字节) | 序列化数据 (N字节) |
+----------------+------------------+----------------+------------------+
  • MyEncode(编码器):将 RPCRequest/RPCResponse 对象按上述格式写入字节流
  • MyDecode(解码器):从字节流中依次读取消息类型、序列化类型、数据长度和数据体,还原为对象
  • MessageType(消息类型枚举)REQUEST(0)RESPONSE(1)

4.6 服务注册与发现

ServiceRegister 接口

定义两个基本功能:注册服务地址、按服务名发现地址。

ZkServiceRegister — ZooKeeper 实现

使用 Apache Curator 操作 ZooKeeper:

  • 根路径/MyRPC
  • 注册:服务名创建为永久节点,服务地址创建为临时节点(服务下线自动删除)
  • 发现:获取服务名下所有子节点(地址列表),通过负载均衡策略选取一个
  • ZooKeeper 地址node1:32181,node2:32181,node3:32181

ZooKeeper 节点结构示例:

/MyRPC
  ├── /com.demo.service.UserService          (永久节点)
  │   └── /127.0.0.1:8899                   (临时节点)
  └── /com.demo.service.BlogService          (永久节点)
      └── /127.0.0.1:8899                   (临时节点)

4.7 负载均衡

LoadBalance 接口

给定服务器地址列表,根据策略选择一个。

内置策略

策略 实现类 说明
随机 RandomLoadBalance 随机选取一个地址
轮询 RoundLoadBalance 依次轮询,默认使用此策略

五、共享服务契约(server-inter 模块)

实体类

字段 说明
User userId, name 用户实体
Blog id, name, desc 博客实体

服务接口

接口 方法 说明
UserService getUserById(String) 根据 ID 查询用户
BlogService getBlogById(Integer) 根据 ID 查询博客
getBlogs() 获取博客列表
printDate() 无返回值测试方法

六、快速开始

前置条件

  1. JDK 22+
  2. Maven 3.x
  3. ZooKeeper 集群(默认地址:node1:32181,node2:32181,node3:32181

启动步骤

1. 编译项目

mvn clean install

2. 启动 ZooKeeper

确保 ZooKeeper 集群已运行。如需修改连接地址,修改 ZkServiceRegister 中的 zkHosts 变量。

3. 启动服务端

运行 server 模块中的 RPCServerApp.main()

服务端会:

  • 创建 UserServiceImplBlogServiceImpl 实例
  • 通过 ServiceProvider 注册到 ZooKeeper(地址 127.0.0.1:8899
  • 启动 Netty 服务器监听 8899 端口

4. 启动客户端

运行 client 模块中的 RPCClient.main()

客户端会:

  • 创建 ClientProxy(使用 NettyRPCClient
  • 通过代理调用远程服务方法

七、Netty Pipeline 架构

客户端 Pipeline

SocketChannel
  └── MyDecode          (解码器:字节流 → RPCResponse)
  └── MyEncode          (编码器:RPCRequest → 字节流)
  └── NettyClientHandler (接收响应,存入 Channel Attribute)

服务端 Pipeline

SocketChannel
  └── MyDecode              (解码器:字节流 → RPCRequest)
  └── MyEncode              (编码器:RPCResponse → 字节流)
  └── NettyRPCServerHandler (处理请求,反射调用服务)

八、扩展指南

添加新的服务

  1. server-inter 模块中定义服务接口和实体类
  2. server 模块中编写实现类
  3. RPCServerApp 中注册实现:
    MyService myService = new MyServiceImpl();
    serviceProvider.provideServiceInterface(myService);
  4. 在客户端通过代理调用:
    MyService proxy = clientProxy.getProxy(MyService.class);
    proxy.someMethod();

添加新的序列化方式

  1. 实现 Serializer 接口,分配新的类型编号
  2. Serializer.getSerializerByCode() 中注册

添加新的负载均衡策略

实现 LoadBalance 接口,并在 ZkServiceRegister 中替换 loadBalance 字段。

切换传输方式

  • 客户端:将 new NettyRPCClient() 替换为 new SimpleRPCClient()
  • 服务端:将 new NettyRPCServer(serviceProvider) 替换为 new ThreadPoolRPCRPCServer(serviceProvider)

About

java手写rpc

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages