DangerWind-RPC-framework---八、Netty网络通信

客户端与服务端之间发送数据需要进行网络通信,而Netty恰好是一款出色的网络通信框架,Netty底层实现依赖于NIO,其工作线程组EventLoopGroup中的每一个EventLoop可以理解为一个线程,与NIO类似,EventLoopGroup的工作也是基于事件响应的,可以理解为bossEventLoop负责监听连接事件,并建立连接,众多workEventLoop监听读事件,在channel可读时读入数据并进行处理。

在写出与读入数据的过程中,数据会经过流水线上的一道道Handler来处理,首先以客户端为例:

java 复制代码
   public NettyRpcClient() {
        // initialize resources such as EventLoopGroup, Bootstrap
        eventLoopGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                //  The timeout period of the connection.
                //  If this time is exceeded or the connection cannot be established, the connection fails.
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ChannelPipeline p = ch.pipeline();
                        // If no data is sent to the server within 15 seconds, a heartbeat request is sent
                        p.addLast(new IdleStateHandler(0, 5, 0, TimeUnit.SECONDS));
                        p.addLast(new RpcMessageEncoder());
                        p.addLast(new RpcMessageDecoder());
                        p.addLast(new NettyRpcClientHandler());
                    }
                });
        this.serviceDiscovery = ExtensionLoader.getExtensionLoader(ServiceDiscovery.class).getExtension(ServiceDiscoveryEnum.ZK.getName());
        this.unprocessedRequests = SingletonFactory.getInstance(UnprocessedRequests.class);
        this.channelProvider = SingletonFactory.getInstance(ChannelProvider.class);
    }

当数据发出时,会被经过的一个个ChannelOutboundHandler(简称out)依次处理,而不会被ChannelInboundHandler(简称in)处理, RpcMessageEncoder对应的是out,因为写出时才会需要编码,RpcMessageDecoder和NettyRpcClientHandler对应的是in,因为读取服务端数据时才需要解码以及处理响应数据。

服务端也是类似:

java 复制代码
   try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    // TCP默认开启了 Nagle 算法,该算法的作用是尽可能的发送大数据快,减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 是否开启 TCP 底层心跳机制
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    // 当客户端第一次进行请求的时候才会进行初始化
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            // 30 秒之内没有收到客户端请求的话就关闭连接
                            ChannelPipeline p = ch.pipeline();
                            p.addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
                            p.addLast(new RpcMessageEncoder());
                            p.addLast(new RpcMessageDecoder());
                            p.addLast(serviceHandlerGroup, new NettyRpcServerHandler());
                        }
                    });

            // 绑定端口,同步等待绑定成功
            ChannelFuture f = b.bind(host, PORT).sync();
            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            log.error("occur exception when start server:", e);
        } finally {
            log.error("shutdown bossGroup and workerGroup");
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            serviceHandlerGroup.shutdownGracefully();
        }

服务端也使用RpcMessageEncoder和RpcMessageDecoder来进行编解码,因为整个框架的编解码需要使用同一套规则。NettyRpcServerHandler自然对应的是in,因为需要在接收读取数据时进行处理,服务端具体通过反射调用执行方法获取执行结果的过程就发生在这里。

除此之外,网络通信还需要解决粘包半包的问题,也需要指定一套通信时的规则并可以被双方感知。因此需要自定义rpc协议,这也是rpc与http的区别之一。这里给出本RPC框架自定义的数据格式:

收发数据的双方均按此格式进行数据的封装解析,就可以避免粘包半包问题的出现,并且也明确了数据的解析方式,便于接收方进行数据的解析与后续的处理。

相关推荐
微风欲寻竹影10 分钟前
Java数据结构——栈(Stack)详解
java·开发语言·数据结构
Makoto_Kimur13 分钟前
Java 后端面试场景题:页面刷新后一直转圈,应该怎么排查?
java·开发语言·面试
ch.ju27 分钟前
Java Programming Chapter 3——Default value of array
java·开发语言
bandaoyu35 分钟前
【CUDA】store/load普通访存 vs 非临时(Non-Temporal)访存
java·开发语言·redis
AI人工智能+电脑小能手35 分钟前
【大白话说Java面试题 第53题】【JVM篇】第13题:JVM采用什么算法判断一个对象是否需要被回收?
java·jvm·算法·面试
逍遥德36 分钟前
常见的任务调度框架介绍
java·spring boot·中间件
Shota Kishi37 分钟前
ERPC 在 Solana RPC 中集成 Pyth Hermes 兼容的 Price API:从架构到调用的技术解析
网络协议·rpc·架构
jiayong2338 分钟前
Memory 写入、检索与纠错机制:让 Agent 记住,也让它忘对
java·服务器·网络·hermes
小赵不会秃头39 分钟前
数据结构Day 06:线性结构、库操作及 Makefile 完整学习笔记
java·linux·数据结构·算法·面试
xqqxqxxq40 分钟前
Maven 完整配置与使用技术笔记
java·笔记·maven