guide-rpc-framework笔记(四):网络通信

117 阅读6分钟

🏗️ 整体架构概览

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

📁 目录结构

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 协议格式:

 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 - 传输消息包装
public class RpcMessage {
    private byte messageType;    // 消息类型
    private byte codec;          // 序列化类型
    private byte compress;       // 压缩类型
    private int requestId;       // 请求ID
    private Object data;         // 消息数据
}
RpcRequest - RPC 请求
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 响应
public class RpcResponse<T> {
    private String requestId;     // 请求ID
    private Integer code;         // 响应码
    private String message;       // 响应消息
    private T data;              // 响应数据
}

🚀 服务端架构

NettyRpcServer - 服务端启动器

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 - 服务端消息处理器

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 - 客户端连接器

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 - 客户端消息处理器

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);
            }
        }
    }
}

🔄 完整调用链路

image.png

调用链路

客户端发起请求流程:
  1. 请求发起 RpcClientProxy.invoke() → NettyRpcClient.sendRpcRequest()

  2. 服务发现 ServiceDiscovery.lookupService() → 获取服务地址

  3. 连接管理 ChannelProvider.get() → 获取或创建连接 NettyRpcClient.doConnect() → 建立新连接(如需要)

  4. 请求发送 构建 RpcMessage → UnprocessedRequests.put() → 注册待处理请求 → Channel.writeAndFlush() → 发送到网络

  5. 编码过程 RpcMessage → RpcMessageEncoder.encode() → ByteBuf

服务端处理流程:
  1. 消息接收 Netty EventLoop → ChannelPipeline

  2. 解码过程 ByteBuf → RpcMessageDecoder.decode() → RpcMessage

  3. 业务处理 NettyRpcServerHandler.channelRead() → RpcRequestHandler.handle() → ServiceProvider.getService() → 获取服务实例 → Method.invoke() → 反射调用目标方法

  4. 响应发送 构建 RpcResponse → 包装为 RpcMessage → RpcMessageEncoder.encode() → Channel.writeAndFlush()

客户端接收响应:
  1. 响应接收 Netty EventLoop → ChannelPipeline

  2. 解码响应 ByteBuf → RpcMessageDecoder.decode() → RpcMessage

  3. 完成请求 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 解决粘包拆包

Demo

代码 demo:github.com/liuyjray/gu…

1. Netty 核心组件架构

Netty 的核心架构基于以下几个关键组件:

1.1 EventLoopGroup(事件循环组)

// 服务端:两个线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);     // 处理连接
EventLoopGroup workerGroup = new NioEventLoopGroup();    // 处理I/O

// 客户端:一个线程组
EventLoopGroup group = new NioEventLoopGroup();

职责分工:

  • bossGroup:专门负责接收客户端连接请求
  • workerGroup:负责处理已建立连接的 I/O 操作

1.2 Bootstrap(启动器)

  • ServerBootstrap:服务端启动器,用于配置和启动服务端
  • Bootstrap:客户端启动器,用于配置和启动客户端

1.3 Channel(通道)

  • NioServerSocketChannel:服务端通道,基于 NIO 的非阻塞服务端套接字
  • NioSocketChannel:客户端通道,基于 NIO 的非阻塞客户端套接字

1.4 ChannelHandler(处理器)

  • ChannelInboundHandler:入站处理器,处理读取数据
  • ChannelOutboundHandler:出站处理器,处理写入数据

2. 简单示例的代码结构

2.1 服务端结构

public void start() throws InterruptedException {
    // 1. 创建线程组
    // bossGroup 用于接收客户端连接
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // workerGroup 用于处理客户端请求
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {
        // 2. 创建服务端启动器
        ServerBootstrap serverBootstrap = new ServerBootstrap();

        // 3. 配置启动器
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)  // 设置服务端 Channel 类型
                .option(ChannelOption.SO_BACKLOG, 128)  // 设置连接队列大小
                .childOption(ChannelOption.SO_KEEPALIVE, true)  // 保持连接活跃
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        // 4. 设置处理器链
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(new EchoServerHandler());
                    }
                });

        // 5. 绑定端口并启动服务
        ChannelFuture future = serverBootstrap.bind(port).sync();
        System.out.println("🚀 Netty 服务端启动成功,监听端口:" + port);

        // 6. 等待服务端关闭
        future.channel().closeFuture().sync();
    } finally {
        // 7. 优雅关闭线程组
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

2.2 客户端结构

public void start() throws InterruptedException {
    // 1. 创建线程组
    EventLoopGroup group = new NioEventLoopGroup();

    try {
        // 2. 创建客户端启动器
        Bootstrap bootstrap = new Bootstrap();

        // 3. 配置启动器
        bootstrap.group(group)
                .channel(NioSocketChannel.class)  // 设置客户端 Channel 类型
                .option(ChannelOption.SO_KEEPALIVE, true)  // 保持连接活跃
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        // 4. 设置处理器链
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new StringDecoder());
                        pipeline.addLast(new StringEncoder());
                        pipeline.addLast(new EchoClientHandler());
                    }
                });

        // 5. 连接服务端
        ChannelFuture future = bootstrap.connect(host, port).sync();
        Channel channel = future.channel();

        // 6. 发送消息
        while (true) {
            String input = scanner.nextLine();
            if ("quit".equalsIgnoreCase(input)) {
                break;
            }
            channel.writeAndFlush(input);
        }
    } finally {
        // 7. 优雅关闭
        group.shutdownGracefully();
    }
}

2.3 消息处理器

服务端处理器:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        String message = (String) msg;
        System.out.println("📨 服务端接收到消息:" + message);

        // 回显消息给客户端
        String response = "服务端回复:" + message;
        ctx.writeAndFlush(response);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        System.out.println("🔗 客户端连接建立:" + ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        System.err.println("💥 服务端发生异常:" + cause.getMessage());
        ctx.close();
    }
}

3. ChannelPipeline 处理器链

ChannelPipeline 是 Netty 的核心概念,它是一个处理器链:

.childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();

        // 添加处理器(按顺序)
        pipeline.addLast(new StringDecoder());    // 解码器
        pipeline.addLast(new StringEncoder());    // 编码器
        pipeline.addLast(new EchoServerHandler()); // 业务处理器
    }
});

3.1 处理器执行顺序

  • 入站(Inbound) :从前往后执行

    网络数据 → StringDecoder → StringEncoder → EchoServerHandler → 业务处理
    
  • 出站(Outbound) :从后往前执行

    业务数据 → EchoServerHandler → StringEncoder → StringDecoder → 网络发送
    

3.2 处理器类型

处理器类型作用典型实现
编解码器数据格式转换StringDecoder, StringEncoder
协议处理器协议解析HttpServerCodec, WebSocketFrameDecoder
业务处理器业务逻辑自定义 Handler
工具处理器辅助功能LoggingHandler, IdleStateHandler