一、RPC 是什么?为什么需要 RPC?
在分布式系统中,我们经常需要调用其他服务器上的方法,比如用户服务需要调用订单服务的 “创建订单” 接口。如果直接通过 HTTP 调用,会面临序列化、网络通信、服务发现、负载均衡等一系列问题。RPC(Remote Procedure Call,远程过程调用) 就是为解决这些问题而生的技术 —— 它能让我们像调用本地方法一样调用远程服务,屏蔽分布式通信的复杂细节。
举个通俗的例子:你在公司电脑上想查看家里电脑的文件,不需要自己手动建立网络连接、处理数据传输,只需要双击 “远程桌面” 图标,就能像操作本地电脑一样操作家里的电脑。RPC 就相当于分布式系统中的 “远程桌面”,让跨服务调用变得简单。
1.1 RPC 的核心价值
- 透明化远程调用:调用远程方法和调用本地方法语法一致,开发者无需关注网络通信细节。
- 高性能通信:相比 HTTP,RPC 通常采用更轻量的协议(如自定义 TCP 协议)和更高效的序列化方式(如 Protobuf、FastJSON2),性能更高。
- 服务化支撑:RPC 框架通常集成服务注册发现、负载均衡、容错等能力,是微服务架构的基础。
1.2 RPC 调用的核心流程
为了让大家直观理解 RPC 的工作原理,我们用 Mermaid 流程图展示一次完整的 RPC 调用过程:

二、RPC 框架的核心架构设计
一个成熟的 RPC 框架需要包含 6 大核心组件,各组件职责清晰、协同工作。我们先通过架构图了解整体结构:

各组件的核心职责如下:
- 服务注册中心:存储服务名称与服务地址的映射关系(如 ZooKeeper、Nacos),支持服务注册和发现。
- 服务提供者:暴露本地服务,将服务信息注册到注册中心,接收并处理消费者的远程调用请求。
- 服务消费者:从注册中心获取服务地址,通过动态代理发起远程调用。
- 动态代理层:为消费者生成服务代理对象,将 “调用代理方法” 转为 “远程调用请求”。
- 网络传输层:负责跨节点的二进制数据传输(通常基于 Netty 的 NIO 模型实现)。
- 序列化层:将 Java 对象与二进制数据相互转换(解决 “对象不能跨网络传输” 的问题)。
三、环境搭建:确定技术栈与依赖
在开始编码前,我们先明确技术栈选型,所有组件均采用最新稳定版本,确保兼容性和性能:
| 组件 | 技术选型 | 版本 | 用途说明 |
|---|
| JDK | Oracle JDK | 17 | 基础开发环境 |
| 项目管理 | Maven | 3.9.6 | 依赖管理和项目构建 |
| 网络通信 | Netty | 4.1.100.Final | 高性能 NIO 通信框架,实现 TCP 传输 |
| 服务注册中心 | ZooKeeper | 3.9.2 | 存储服务地址,支持服务发现 |
| 序列化 | FastJSON2 | 2.0.48 | 高效 JSON 序列化 / 反序列化框架 |
| 动态代理 | JDK 动态代理 | 内置 | 生成服务代理对象 |
| 工具类 | Lombok、Spring Utils | Lombok 1.18.30 | 简化代码(@Slf4j)、判空工具等 |
| 接口文档 | Swagger3 | 2.2.0 | 生成 API 文档,方便测试 |
| 持久层(可选) | MyBatis-Plus | 3.5.5 | 若服务需要操作数据库,用于数据访问 |
3.1 核心依赖配置(pom.xml)
创建 Maven 项目,在pom.xml中添加以下依赖,所有版本均为 2024 年最新稳定版:
4.0.0
com.ken.rpc
ken-rpc-framework
1.0.0
17
17
UTF-8
4.1.100.Final
3.9.2
2.0.48
1.18.30
6.1.5
2.2.0
3.5.5
8.0.36
org.projectlombok
lombok
${lombok.version}
provided
org.springframework
spring-context
${spring-context.version}
com.google.guava
guava
33.1.0-jre
io.netty
netty-all
${netty.version}
org.apache.zookeeper
zookeeper
${zookeeper.version}
org.slf4j
slf4j-log4j12
log4j
log4j
com.alibaba.fastjson2
fastjson2
${fastjson2.version}
org.springdoc
springdoc-openapi-starter-webmvc-ui
${swagger.version}
com.baomidou
mybatis-plus-boot-starter
${mybatis-plus.version}
mysql
mysql-connector-java
${mysql.version}
runtime
四、核心模块实现:从底层到上层
我们按照 “底层支撑→核心逻辑→上层应用” 的顺序实现 RPC 框架,每个模块都包含 “设计思路 + 可运行代码”,确保读者能直接复用。
4.1 序列化层:解决 “对象跨网络传输” 问题
设计思路:Java 对象不能直接通过网络传输,需要先转为二进制数据(序列化);服务端接收后,再将二进制转成 Java 对象(反序列化)。我们选择 FastJSON2 作为序列化工具(相比 JSON、Protobuf,它兼顾性能和易用性),定义统一的序列化接口,方便后续替换其他序列化方式。
4.1.1 序列化接口定义
package com.ken.rpc.serialize;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.ken.rpc.exception.SerializeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
/**
* 序列化工具类(基于FastJSON2实现)
* 负责Java对象与二进制数据的相互转换
*
* @author ken
*/
@Slf4j
public class FastJson2Serializer implements Serializer {
/**
* 序列化:将Java对象转为字节数组
*
* @param obj 待序列化的对象
* @return 序列化后的字节数组
* @throws SerializeException 序列化失败时抛出
*/
@Override
public byte[] serialize(Object obj) throws SerializeException {
if (ObjectUtils.isEmpty(obj)) {
log.error("序列化对象为空,无法执行序列化操作");
throw new SerializeException("序列化对象不能为空");
}
try {
// FastJSON2序列化:对象→JSON字符串→字节数组(指定UTF-8编码)
return JSON.toJSONBytes(obj);
} catch (Exception e) {
log.error("对象序列化失败,对象类型:{},异常信息:{}", obj.getClass().getName(), e.getMessage(), e);
throw new SerializeException("序列化失败:" + e.getMessage());
}
}
/**
* 反序列化:将字节数组转为指定类型的Java对象
*
* @param bytes 待反序列化的字节数组
* @param clazz 目标对象的Class类型
* @param 目标对象的泛型
* @return 反序列化后的Java对象
* @throws SerializeException 反序列化失败时抛出
*/
@Override
public T deserialize(byte[] bytes, Class clazz) throws SerializeException {
if (ObjectUtils.isEmpty(bytes)) {
log.error("反序列化字节数组为空,无法执行反序列化操作");
throw new SerializeException("反序列化字节数组不能为空");
}
if (ObjectUtils.isEmpty(clazz)) {
log.error("反序列化目标类型为空,无法执行反序列化操作");
throw new SerializeException("反序列化目标类型不能为空");
}
try {
// FastJSON2反序列化:字节数组→指定类型对象
return JSON.parseObject(bytes, clazz);
} catch (Exception e) {
log.error("字节数组反序列化失败,目标类型:{},异常信息:{}", clazz.getName(), e.getMessage(), e);
throw new SerializeException("反序列化失败:" + e.getMessage());
}
}
/**
* 反序列化(支持泛型类型,如List、Map)
*
* @param bytes 待反序列化的字节数组
* @param typeReference FastJSON2的TypeReference,用于指定泛型类型
* @param 目标对象的泛型
* @return 反序列化后的Java对象
* @throws SerializeException 反序列化失败时抛出
*/
@Override
public T deserialize(byte[] bytes, TypeReference typeReference) throws SerializeException {
if (ObjectUtils.isEmpty(bytes)) {
log.error("反序列化字节数组为空,无法执行反序列化操作");
throw new SerializeException("反序列化字节数组不能为空");
}
if (ObjectUtils.isEmpty(typeReference)) {
log.error("反序列化泛型类型为空,无法执行反序列化操作");
throw new SerializeException("反序列化泛型类型不能为空");
}
try {
// 支持泛型的反序列化(如List)
return JSON.parseObject(bytes, typeReference);
} catch (Exception e) {
log.error("字节数组泛型反序列化失败,异常信息:{}", e.getMessage(), e);
throw new SerializeException("泛型反序列化失败:" + e.getMessage());
}
}
}
4.1.2 序列化异常定义
为了统一异常处理,定义序列化相关的自定义异常:
package com.ken.rpc.exception;
/**
* 序列化/反序列化异常
* 当对象与字节数组转换过程中出现错误时抛出
*
* @author ken
*/
public class SerializeException extends RuntimeException {
public SerializeException(String message) {
super(message);
}
public SerializeException(String message, Throwable cause) {
super(message, cause);
}
}
4.2 网络传输层:基于 Netty 实现高性能通信
设计思路:网络传输是 RPC 的核心底层支撑,需要解决 “高并发”“低延迟” 问题。Netty 是基于 NIO 的高性能通信框架,我们用它实现 TCP 服务端(服务提供者)和客户端(服务消费者),并定义统一的 “请求 / 响应” 数据结构,确保数据传输的完整性。
4.2.1 定义 RPC 请求 / 响应实体
首先定义 RPC 调用的 “请求” 和 “响应” 格式,所有网络传输的数据都遵循这个结构:
package com.ken.rpc.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* RPC请求实体
* 封装消费者向提供者发送的调用信息
*
* @author ken
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RpcRequest implements Serializable {
/**
* 请求ID:唯一标识一次RPC调用(用于解决请求与响应的对应问题)
*/
private String requestId;
/**
* 服务接口名称(如com.ken.rpc.service.UserService)
* 用于服务提供者找到对应的实现类
*/
private String serviceName;
/**
* 方法名称(如getUserById)
*/
private String methodName;
/**
* 方法参数类型列表(如[java.lang.Long])
* 用于服务提供者找到重载的方法
*/
private Class[] parameterTypes;
/**
* 方法参数值列表(如[1001])
*/
private Object[] parameters;
/**
* 服务版本号(用于处理服务升级,如1.0.0、2.0.0)
*/
private String serviceVersion;
}
package com.ken.rpc.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* RPC响应实体
* 封装服务提供者向消费者返回的结果
*
* @author ken
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RpcResponse implements Serializable {
/**
* 请求ID:与RpcRequest的requestId对应,确保响应匹配
*/
private String requestId;
/**
* 调用成功:返回的方法执行结果
*/
private Object result;
/**
* 调用失败:返回的异常信息
*/
private Throwable exception;
/**
* 判断本次RPC调用是否成功
*
* @return true-成功,false-失败
*/
public boolean isSuccess() {
return exception == null;
}
}
4.2.2 Netty 服务端实现(服务提供者)
服务提供者需要启动 Netty 服务端,监听指定端口,接收消费者的请求并处理:
package com.ken.rpc.transport.netty;
import com.ken.rpc.entity.RpcRequest;
import com.ken.rpc.entity.RpcResponse;
import com.ken.rpc.handler.RpcRequestHandler;
import com.ken.rpc.serialize.FastJson2Serializer;
import com.ken.rpc.serialize.Serializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* Netty RPC服务端
* 负责启动服务、监听端口、接收并处理消费者的RPC请求
*
* @author ken
*/
@Slf4j
public class NettyRpcServer {
/**
* 服务端口号
*/
private final int port;
/**
* 服务注册中心地址(如zookeeper://127.0.0.1:2181)
*/
private final String registryAddress;
/**
* 序列化工具(默认使用FastJSON2)
*/
private final Serializer serializer;
/**
* RPC请求处理器(负责调用本地服务方法)
*/
private final RpcRequestHandler rpcRequestHandler;
public NettyRpcServer(int port, String registryAddress) {
this(port, registryAddress, new FastJson2Serializer(), new RpcRequestHandler());
}
public NettyRpcServer(int port, String registryAddress, Serializer serializer, RpcRequestHandler rpcRequestHandler) {
this.port = port;
this.registryAddress = registryAddress;
this.serializer = serializer;
this.rpcRequestHandler = rpcRequestHandler;
}
/**
* 启动Netty服务端
*/
public void start() {
// 1. 校验参数
if (port <= 0 || port > 65535) {
log.error("服务端口号非法,端口:{}(合法范围:1-65535)", port);
throw new IllegalArgumentException("服务端口号非法:" + port);
}
if (!StringUtils.hasText(registryAddress)) {
log.error("服务注册中心地址为空,无法启动服务");
throw new IllegalArgumentException("服务注册中心地址不能为空");
}
// 2. 初始化Netty线程组
// BossGroup:负责接收客户端连接
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// WorkerGroup:负责处理客户端的IO请求
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 3. 配置服务端启动参数
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
// 配置服务端通道类型(NIO)
.channel(NioServerSocketChannel.class)
// 日志打印(DEBUG级别,方便调试)
.handler(new LoggingHandler(LogLevel.DEBUG))
// 配置客户端连接的通道初始化器
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// ① 粘包/拆包处理器:基于消息长度字段解决TCP粘包问题
// 解码:读取消息时,先读取4字节的长度字段,再读取对应长度的消息体
pipeline.addLast(new LengthFieldBasedFrameDecoder(
Integer.MAX_VALUE, 0, 4, 0, 4));
// 编码:发送消息时,在消息体前添加4字节的长度字段
pipeline.addLast(new LengthFieldPrepender(4));
// ② 序列化/反序列化处理器:将字节数组转为Java对象(请求),将Java对象转为字节数组(响应)
pipeline.addLast(new NettySerializerHandler(serializer, RpcRequest.class, RpcResponse.class));
// ③ RPC请求处理器:处理请求,调用本地服务方法,生成响应
pipeline.addLast(new NettyRpcServerHandler(rpcRequestHandler));
}
})
// 服务端接收连接的队列大小
.option(ChannelOption.SO_BACKLOG, 128)
// 保持TCP连接(防止连接超时)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 4. 绑定端口,启动服务(同步阻塞,直到服务启动完成)
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
log.info("Netty RPC服务端启动成功,监听端口:{},注册中心地址:{}", port, registryAddress);
// 5. 注册服务到注册中心(后续章节实现)
registerService();
// 6. 阻塞等待通道关闭(服务停止时才会继续执行)
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
log.error("Netty RPC服务端启动过程被中断,异常信息:{}", e.getMessage(), e);
} finally {
// 7. 优雅关闭线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
log.info("Netty RPC服务端已关闭");
}
}
/**
* 注册服务到注册中心(后续章节实现服务注册逻辑)
*/
private void registerService() {
try {
// 获取本地IP地址(服务提供者的IP)
String localIp = InetAddress.getLocalHost().getHostAddress();
log.info("准备注册服务到注册中心,服务地址:{}:{}", localIp, port);
// TODO:后续章节实现ZooKeeper注册逻辑
} catch (UnknownHostException e) {
log.error("获取本地IP地址失败,无法注册服务,异常信息:{}", e.getMessage(), e);
throw new RuntimeException("获取本地IP地址失败:" + e.getMessage());
}
}
}
4.2.3 Netty 客户端实现(服务消费者)
服务消费者需要通过 Netty 客户端发送 RPC 请求,并接收响应:
package com.ken.rpc.transport.netty;
import com.ken.rpc.entity.RpcRequest;
import com.ken.rpc.entity.RpcResponse;
import com.ken.rpc.serialize.FastJson2Serializer;
import com.ken.rpc.serialize.Serializer;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Netty RPC客户端
* 负责与服务提供者建立连接,发送RPC请求,接收响应
*
* @author ken
*/
@Slf4j
public class NettyRpcClient {
/**
* 服务端地址(IP:端口)
*/
private final InetSocketAddress serverAddress;
/**
* 序列化工具(默认使用FastJSON2)
*/
private final Serializer serializer;
/**
* 存储未处理的RPC请求(key:requestId,value:CompletableFuture)
* 用于将响应与请求匹配(Netty是异步通信,需要通过requestId关联)
*/
private final ConcurrentMap> unprocessedRequests;
/**
* Netty客户端通道(与服务端的连接通道)
*/
private Channel channel;
/**
* Netty事件循环组(负责处理IO事件)
*/
private final EventLoopGroup eventLoopGroup;
public NettyRpcClient(InetSocketAddress serverAddress) {
this(serverAddress, new FastJson2Serializer());
}
public NettyRpcClient(InetSocketAddress serverAddress, Serializer serializer) {
this.serverAddress = serverAddress;
this.serializer = serializer;
this.unprocessedRequests = new ConcurrentHashMap<>();
this.eventLoopGroup = new NioEventLoopGroup();
// 初始化客户端连接
this.initialize();
}
/**
* 初始化Netty客户端,与服务端建立连接
*/
private void initialize() {
// 1. 校验服务端地址
if (ObjectUtils.isEmpty(serverAddress) || StringUtils.isEmpty(serverAddress.getHostName())) {
log.error("服务端地址非法,无法建立连接");
throw new IllegalArgumentException("服务端地址非法");
}
try {
// 2. 配置客户端启动参数
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
// 客户端通道类型(NIO)
.channel(NioSocketChannel.class)
// 连接超时时间(3秒)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000)
// 保持TCP连接
.option(ChannelOption.SO_KEEPALIVE, true)
// 禁用Nagle算法(减少延迟,适合RPC小数据传输)
.option(ChannelOption.TCP_NODELAY, true)
// 配置通道初始化器
.handler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// ① 粘包/拆包处理器(与服务端对应)
pipeline.addLast(new LengthFieldBasedFrameDecoder(
Integer.MAX_VALUE, 0, 4, 0, 4));
pipeline.addLast(new LengthFieldPrepender(4));
// ② 序列化/反序列化处理器(与服务端对应)
pipeline.addLast(new NettySerializerHandler(serializer, RpcRequest.class, RpcResponse.class));
// ③ 客户端响应处理器(接收服务端的响应,完成CompletableFuture)
pipeline.addLast(new NettyRpcClientHandler(unprocessedRequests));
}
});
// 3. 与服务端建立连接(同步阻塞,直到连接建立完成)
ChannelFuture channelFuture = bootstrap.connect(serverAddress).sync();
log.info("Netty RPC客户端与服务端建立连接成功,服务端地址:{}:{}",
serverAddress.getHostName(), serverAddress.getPort());
// 4. 保存通道引用,用于后续发送请求
channel = channelFuture.channel();
// 5. 监听通道关闭事件(连接断开时打印日志)
channel.closeFuture().addListener(future -> {
log.info("Netty RPC客户端与服务端连接断开,服务端地址:{}:{}",
serverAddress.getHostName(), serverAddress.getPort());
eventLoopGroup.shutdownGracefully();
});
} catch (InterruptedException e) {
log.error("Netty RPC客户端建立连接过程被中断,服务端地址:{}:{},异常信息:{}",
serverAddress.getHostName(), serverAddress.getPort(), e.getMessage(), e);
eventLoopGroup.shutdownGracefully();
}
}
/**
* 发送RPC请求,返回CompletableFuture(异步获取响应)
*
* @param rpcRequest RPC请求实体
* @return 包含RPC响应的CompletableFuture
*/
public CompletableFuture sendRequest(RpcRequest rpcRequest) {
// 1. 校验请求和通道
if (ObjectUtils.isEmpty(rpcRequest) || StringUtils.isEmpty(rpcRequest.getRequestId())) {
log.error("RPC请求非法(请求ID为空),无法发送请求");
throw new IllegalArgumentException("RPC请求ID不能为空");
}
if (ObjectUtils.isEmpty(channel) || !channel.isActive()) {
log.error("客户端通道未建立或已关闭,无法发送请求,服务端地址:{}:{}",
serverAddress.getHostName(), serverAddress.getPort());
throw new RuntimeException("客户端通道不可用,无法发送请求");
}
// 2. 创建CompletableFuture,用于接收响应
CompletableFuture resultFuture = new CompletableFuture<>();
// 3. 将请求ID与CompletableFuture关联,存入未处理请求Map
unprocessedRequests.put(rpcRequest.getRequestId(), resultFuture);
// 4. 发送请求(Netty的writeAndFlush是异步操作,不会阻塞)
channel.writeAndFlush(rpcRequest).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
log.info("RPC请求发送成功,请求ID:{},服务名称:{},方法名称:{}",
rpcRequest.getRequestId(), rpcRequest.getServiceName(), rpcRequest.getMethodName());
} else {
// 发送失败:移除未处理请求,完成CompletableFuture(异常)
unprocessedRequests.remove(rpcRequest.getRequestId());
resultFuture.completeExceptionally(future.cause());
log.error("RPC请求发送失败,请求ID:{},异常信息:{}",
rpcRequest.getRequestId(), future.cause().getMessage(), future.cause());
}
});
return resultFuture;
}
/**
* 关闭客户端(优雅关闭线程组和通道)
*/
public void close() {
if (!ObjectUtils.isEmpty(channel) && channel.isActive()) {
channel.close();
}
eventLoopGroup.shutdownGracefully();
log.info("Netty RPC客户端已关闭,服务端地址:{}:{}",
serverAddress.getHostName(), serverAddress.getPort());
}
}
4.2.4 序列化 / 反序列化处理器
Netty 的ChannelHandler负责在通道中处理数据,我们需要自定义处理器,将 Netty 的ByteBuf与 RPC 的RpcRequest/RpcResponse相互转换:
package com.ken.rpc.transport.netty;
import com.ken.rpc.entity.RpcRequest;
import com.ken.rpc.entity.RpcResponse;
import com.ken.rpc.serialize.Serializer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.ReplayingDecoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;
import java.util.List;
/**
* Netty序列化/反序列化处理器
* 包含编码器(Request→ByteBuf)和解码器(ByteBuf→Response)
*
* @author ken
*/
@Slf4j
public class NettySerializerHandler {
/**
* RPC请求编码器:将RpcRequest对象转为ByteBuf(发送给服务端)
*/
public static class RequestEncoder extends MessageToByteEncoder {
private final Serializer serializer;
public RequestEncoder(Serializer serializer) {
this.serializer = serializer;
}
@Override
protected void encode(ChannelHandlerContext ctx, RpcRequest msg, ByteBuf out) throws Exception {
if (ObjectUtils.isEmpty(msg)) {
log.error("RPC请求为空,无法编码");
throw new IllegalArgumentException("RPC请求不能为空");
}
// 1. 序列化RpcRequest为字节数组
byte[] bytes = serializer.serialize(msg);
// 2. 将字节数组写入ByteBuf(Netty会自动处理后续传输)
out.writeBytes(bytes);
log.debug("RPC请求编码完成,请求ID:{},字节长度:{}", msg.getRequestId(), bytes.length);
}
}
/**
* RPC响应解码器:将ByteBuf转为RpcResponse对象(接收服务端响应)
* 使用ReplayingDecoder简化解码逻辑(无需手动处理半包问题)
*/
public static class ResponseDecoder extends ReplayingDecoder {
private final Serializer serializer;
public ResponseDecoder(Serializer serializer) {
this.serializer = serializer;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List