学习Netty(二)------Netty 启动过程与初始化

文章目录

  • 启动流程:
  • 组件初始化:
  • 代码示例:

启动流程:

Netty的启动过程涉及多个关键组件,其中ServerBootstrap是入口。在启动流程中,我们通过以下步骤逐一深入了解:
1.创建 EventLoopGroup:
在启动过程中,首先需要创建两个 EventLoopGroup 实例,分别用于处理连接(bossGroup)和处理业务逻辑(workerGroup)。

EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();

这两个 EventLoopGroup 使用 NIO 进行事件处理,NioEventLoopGroup 内部会创建一定数量的 NioEventLoop 实例,每个实例运行在一个单独的线程上。
2 配置 ServerBootstrap
创建 ServerBootstrap 实例,将 bossGroup 和 workerGroup 配置到其中,并设置服务器的通信协议和处理器。

ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .childHandler(new YourChannelInitializer());
// ... 其他配置

在这一步,通过 channel 方法指定使用 NIO 传输,childHandler 方法设置一个 ChannelInitializer,用于初始化新连接的 ChannelPipeline。
3 绑定端口并启动服务器
调用 bind 方法绑定端口,并通过 sync 方法等待绑定完成。在端口绑定完成后,通过 closeFuture 方法等待服务器关闭。

serverBootstrap.bind(8080).sync().channel().closeFuture().sync();

这一步启动了整个 Netty 服务器,服务器开始监听端口,等待客户端连接。

组件初始化:

在 Netty 中,Channel 和 EventLoop 是关键的组件。它们的初始化过程在 ChannelInitializer 中进行。让我们深入了解这两个组件的初始化过程。
1 Channel 初始化
Channel 的初始化过程主要包括创建、注册、活跃和关闭等阶段。在 ChannelInitializer 的 initChannel 方法中,我们可以通过 ChannelPipeline 对 Channel 进行初始化。

public class YourChannelInitializer extends ChannelInitializer<SocketChannel> { 
 
  


    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        // 添加业务逻辑处理器
        pipeline.addLast(new YourBusinessLogicHandler());
    }
}

在这个示例中,我们通过 ChannelPipeline 添加了一个业务逻辑处理器,该处理器会处理从客户端接收到的数据。
2 EventLoop 初始化
EventLoop 在初始化时会创建任务队列,并启动一个线程用于执行任务。NioEventLoop 的初始化过程涉及到创建 Selector 和绑定到当前线程。具体的初始化过程在 NioEventLoop 的构造函数中,涉及到线程的创建、SelectorProvider 的选择以及任务队列的初始化。

public NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider) {
    // ... 其他初始化
    this.selector = openSelector();
    // ... 其他初始化
}

代码示例:

编写一个完整的 Netty 客户端和服务端示例涉及多个步骤,包括创建 ServerBootstrap、配置 EventLoopGroup、实现 ChannelInitializer 等。以下是一个简单的示例,覆盖了这些基本步骤,并尽可能深入地解释了每一步。
服务端代码:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class NettyServer {

    public static void main(String[] args) {
        // 创建两个 EventLoopGroup,bossGroup 用于接收连接,workerGroup 用于处理连接上的业务逻辑
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建 ServerBootstrap 对象,用于配置服务器
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // 指定使用 NIO 传输
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new NettyServerHandler()); // 添加业务逻辑处理器
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置队列大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // 保持连接

            // 绑定端口并启动服务器
            serverBootstrap.bind(8080).sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 优雅地关闭 EventLoopGroup
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务端业务逻辑处理器 NettyServerHandler:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 读取客户端发送的数据
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] data = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(data);
        System.out.println("Server received: " + new String(data));

        // 向客户端发送数据
        ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("Hello, Client!".getBytes()));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理
        cause.printStackTrace();
        ctx.close();
    }
}

客户端代码:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class NettyClient {

    public static void main(String[] args) {
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建 Bootstrap 对象,用于配置客户端
            Bootstrap clientBootstrap = new Bootstrap();
            clientBootstrap.group(workerGroup)
                    .channel(NioSocketChannel.class) // 指定使用 NIO 传输
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new NettyClientHandler()); // 添加业务逻辑处理器
                        }
                    })
                    .option(ChannelOption.SO_KEEPALIVE, true); // 保持连接

            // 连接到服务器
            clientBootstrap.connect("localhost", 8080).sync().channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 优雅地关闭 EventLoopGroup
            workerGroup.shutdownGracefully();
        }
    }
}

客户端业务逻辑处理器 NettyClientHandler:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class NettyClientHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 客户端连接激活时发送数据
        ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("Hello, Server!".getBytes()));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 读取服务端发送的数据
        ByteBuf byteBuf = (ByteBuf) msg;
        byte[] data = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(data);
        System.out.println("Client received: " + new String(data));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理
        cause.printStackTrace();
        ctx.close();
    }
}

运行过程:
1.启动服务端: 运行 NettyServer 类,服务端开始监听端口 8080。
2.启动客户端: 运行 NettyClient 类,客户端连接到服务端。
3.数据交互: 客户端连接成功后,会触发 channelActive 方法,向服务端发送数据。服务端收到数据后,会触发 channelRead 方法,处理数据并向客户端发送响应。客户端收到响应后,会触发 channelRead 方法,处理服务端的响应数据。
4.关闭连接: 当数据交互完成后,客户端和服务端都可以通过 ctx.close() 主动关闭连接。在示例中,客户端和服务端分别在异常处理中关闭连接。
这个简单的示例演示了 Netty 客户端和服务端的基本交互过程。在实际应用中,你可能需要根据具体业务需求添加更多的处理器、编解码器等组件,以满足特定的需求。

你可能感兴趣的:(学习,java,github,http,后端,开发语言,jvm)