guide-rpc-framework笔记(四):网络编程

🏗️ 整体架构概览

这是一个基于 Netty 的 RPC 框架,采用了经典的客户端-服务端架构,主要包含以下几个核心模块:

📁 目录结构

bash 复制代码
rpc-framework-simple/src/main/java/github/javaguide/remoting/transport/netty/
├── codec/           # 编解码器
│   ├── RpcMessageEncoder.java
│   └── RpcMessageDecoder.java
├── client/          # 客户端组件
│   ├── NettyRpcClient.java
│   ├── NettyRpcClientHandler.java
│   ├── ChannelProvider.java
│   └── UnprocessedRequests.java
└── server/          # 服务端组件
    ├── NettyRpcServer.java
    └── NettyRpcServerHandler.java

🔧 核心组件分析

1. 协议设计

框架定义了自定义的 RPC 协议格式:

sql 复制代码
 0     1     2     3     4        5     6     7     8         9          10      11     12  13  14   15 16
 +-----+-----+-----+-----+--------+----+----+----+------+-----------+-------+----- --+-----+-----+-------+
 |   magic   code        |version | full length         | messageType| codec|compress|    RequestId       |
 +-----------------------+--------+---------------------+-----------+-----------+-----------+------------+
 |                                         body                                                          |
 +-------------------------------------------------------------------------------------------------------+
  • 4B magic code(魔法数)
  • 1B version(版本)
  • 4B full length(消息长度)
  • 1B messageType(消息类型)
  • 1B compress(压缩类型)
  • 1B codec(序列化类型)
  • 4B requestId(请求ID)
  • body(消息体)

2. 数据传输对象

RpcMessage - 传输消息包装

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/dto/RpcMessage.java 复制代码
public class RpcMessage {
    private byte messageType;    // 消息类型
    private byte codec;          // 序列化类型
    private byte compress;       // 压缩类型
    private int requestId;       // 请求ID
    private Object data;         // 消息数据
}

RpcRequest - RPC 请求

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/dto/RpcRequest.java 复制代码
public class RpcRequest {
    private String requestId;     // 请求ID
    private String interfaceName; // 接口名
    private String methodName;    // 方法名
    private Object[] parameters;  // 参数
    private Class<?>[] paramTypes; // 参数类型
    private String version;       // 版本
    private String group;         // 分组
}

RpcResponse - RPC 响应

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/dto/RpcResponse.java 复制代码
public class RpcResponse<T> {
    private String requestId;     // 请求ID
    private Integer code;         // 响应码
    private String message;       // 响应消息
    private T data;              // 响应数据
}

🚀 服务端架构

NettyRpcServer - 服务端启动器

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/transport/netty/server/NettyRpcServer.java 复制代码
public class NettyRpcServer {
    public static final int PORT = 9998;
    
    public void start() {
        // 1. 创建线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        DefaultEventExecutorGroup serviceHandlerGroup = new DefaultEventExecutorGroup(
            RuntimeUtil.cpus() * 2,
            ThreadPoolFactoryUtil.createThreadFactory("service-handler-group", false)
        );
        
        // 2. 配置服务端启动器
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
         .channel(NioServerSocketChannel.class)
         .childOption(ChannelOption.TCP_NODELAY, true)
         .childOption(ChannelOption.SO_KEEPALIVE, true)
         .option(ChannelOption.SO_BACKLOG, 128)
         .childHandler(new ChannelInitializer<SocketChannel>() {
             @Override
             protected void initChannel(SocketChannel ch) {
                 ChannelPipeline p = ch.pipeline();
                 // 3. 配置处理器链
                 p.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                 p.addLast(new RpcMessageEncoder());
                 p.addLast(new RpcMessageDecoder());
                 p.addLast(serviceHandlerGroup, new NettyRpcServerHandler());
             }
         });
    }
}

NettyRpcServerHandler - 服务端消息处理器

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/transport/netty/server/NettyRpcServerHandler.java 复制代码
public class NettyRpcServerHandler extends ChannelInboundHandlerAdapter {
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof RpcMessage) {
            RpcMessage rpcMessage = (RpcMessage) msg;
            byte messageType = rpcMessage.getMessageType();
            
            if (messageType == RpcConstants.HEARTBEAT_REQUEST_TYPE) {
                // 处理心跳请求
                rpcMessage.setMessageType(RpcConstants.HEARTBEAT_RESPONSE_TYPE);
                rpcMessage.setData(RpcConstants.PONG);
            } else {
                // 处理RPC请求
                RpcRequest rpcRequest = (RpcRequest) rpcMessage.getData();
                Object result = rpcRequestHandler.handle(rpcRequest);
                
                RpcResponse<Object> rpcResponse = RpcResponse.success(result, rpcRequest.getRequestId());
                rpcMessage.setData(rpcResponse);
            }
            
            ctx.writeAndFlush(rpcMessage);
        }
    }
}

📱 客户端架构

NettyRpcClient - 客户端连接器

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/transport/netty/client/NettyRpcClient.java 复制代码
public class NettyRpcClient implements RpcRequestTransport {
    
    public NettyRpcClient() {
        // 1. 初始化客户端启动器
        eventLoopGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline p = ch.pipeline();
                        // 2. 配置处理器链
                        p.addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS));
                        p.addLast(new RpcMessageEncoder());
                        p.addLast(new RpcMessageDecoder());
                        p.addLast(new NettyRpcClientHandler());
                    }
                });
    }
    
    @Override
    public Object sendRpcRequest(RpcRequest rpcRequest) {
        CompletableFuture<RpcResponse<Object>> resultFuture = new CompletableFuture<>();
        
        // 1. 获取服务地址
        InetSocketAddress inetSocketAddress = serviceDiscovery.lookupService(rpcRequest);
        
        // 2. 获取或创建连接
        Channel channel = getChannel(inetSocketAddress);
        
        if (channel.isActive()) {
            // 3. 发送请求
            unprocessedRequests.put(rpcRequest.getRequestId(), resultFuture);
            RpcMessage rpcMessage = RpcMessage.builder()
                    .data(rpcRequest)
                    .codec(SerializationTypeEnum.HESSIAN.getCode())
                    .compress(CompressTypeEnum.GZIP.getCode())
                    .messageType(RpcConstants.REQUEST_TYPE)
                    .build();
            
            channel.writeAndFlush(rpcMessage);
        }
        
        // 4. 等待响应
        return resultFuture.get();
    }
}

NettyRpcClientHandler - 客户端消息处理器

java:rpc-framework-simple/src/main/java/github/javaguide/remoting/transport/netty/client/NettyRpcClientHandler.java 复制代码
public class NettyRpcClientHandler extends ChannelInboundHandlerAdapter {
    
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof RpcMessage) {
            RpcMessage tmp = (RpcMessage) msg;
            byte messageType = tmp.getMessageType();
            
            if (messageType == RpcConstants.HEARTBEAT_RESPONSE_TYPE) {
                // 处理心跳响应
                log.info("heart [{}]", tmp.getData());
            } else if (messageType == RpcConstants.RESPONSE_TYPE) {
                // 处理RPC响应
                RpcResponse<Object> rpcResponse = (RpcResponse<Object>) tmp.getData();
                unprocessedRequests.complete(rpcResponse);
            }
        }
    }
}

🔄 完整调用链路

🔄 完整调用链路

客户端发起请求流程:

  1. 请求发起

    scss 复制代码
    RpcClientProxy.invoke() → NettyRpcClient.sendRpcRequest()
  2. 服务发现

    scss 复制代码
    ServiceDiscovery.lookupService() → 获取服务地址
  3. 连接管理

    scss 复制代码
    ChannelProvider.get() → 获取或创建连接
    NettyRpcClient.doConnect() → 建立新连接(如需要)
  4. 请求发送

    scss 复制代码
    构建 RpcMessage → 
    UnprocessedRequests.put() → 注册待处理请求 →
    Channel.writeAndFlush() → 发送到网络
  5. 编码过程

    scss 复制代码
    RpcMessage → RpcMessageEncoder.encode() → ByteBuf

服务端处理流程:

  1. 消息接收

    复制代码
    Netty EventLoop → ChannelPipeline
  2. 解码过程

    scss 复制代码
    ByteBuf → RpcMessageDecoder.decode() → RpcMessage
  3. 业务处理

    scss 复制代码
    NettyRpcServerHandler.channelRead() →
    RpcRequestHandler.handle() →
    ServiceProvider.getService() → 获取服务实例 →
    Method.invoke() → 反射调用目标方法
  4. 响应发送

    scss 复制代码
    构建 RpcResponse → 
    包装为 RpcMessage →
    RpcMessageEncoder.encode() →
    Channel.writeAndFlush()

客户端接收响应:

  1. 响应接收

    复制代码
    Netty EventLoop → ChannelPipeline
  2. 解码响应

    scss 复制代码
    ByteBuf → RpcMessageDecoder.decode() → RpcMessage
  3. 完成请求

    scss 复制代码
    NettyRpcClientHandler.channelRead() →
    UnprocessedRequests.complete() →
    CompletableFuture.complete() →
    返回结果给调用方

🛠️ 关键特性

1. 连接管理

  • ChannelProvider:管理客户端到服务端的连接池
  • 连接复用:相同地址复用已建立的连接
  • 连接检测:自动检测连接状态,失效时重新建立

2. 异步处理

  • UnprocessedRequests:管理未完成的请求
  • CompletableFuture:实现异步请求-响应匹配
  • 通过 requestId 关联请求和响应

3. 心跳机制

  • 客户端:5秒无写操作时发送心跳
  • 服务端:30秒无读操作时关闭连接
  • 保证连接活性,及时清理死连接

4. 线程模型

  • Boss线程组:处理连接接受
  • Worker线程组:处理I/O操作
  • 业务线程组:处理具体的RPC调用(避免阻塞I/O线程)

5. 编解码

  • 自定义协议格式
  • 支持多种序列化方式(Hessian、Protostuff等)
  • 支持压缩(GZIP)
  • 基于 LengthFieldBasedFrameDecoder 解决粘包拆包

这个框架设计简洁而完整,很好地展示了如何使用 Netty 构建一个高性能的 RPC 框架。通过分层设计、异步处理和连接复用等技术,实现了高效的远程服务调用能力。

相关推荐
Moonbit6 小时前
安装Wasm版的MoonBit工具链
后端·node.js·webassembly
法欧特斯卡雷特6 小时前
从 Kotlin 编译器 API 的变化开始: 2.2.2X -> 2.3.0-Beta1
后端·架构·开源
zjjuejin6 小时前
Maven 现代开发流程的集成
java·后端·maven
hrrrrb6 小时前
【Spring Boot】Spring Boot 中常见的加密方案
java·spring boot·后端
Lilian6 小时前
Trae通过ssh远程访问服务器linux系统不兼容问题
后端·trae
12344526 小时前
Spring Boot 启动流程全解析:从 SpringApplication.run() 到 Bean 初始化与自动配置
后端
Java中文社群7 小时前
我的网站被攻击了!
后端
程序定小飞7 小时前
基于springboot的在线商城系统设计与开发
java·数据库·vue.js·spring boot·后端
shengjk17 小时前
一文搞懂 java 中 POJO 和 bean 区别
后端