Netty 编程模型

32 阅读4分钟

Netty 编程模型,netty应用入门。

NIO

服务端:ServerSocketChannel监听多个SocketChannel的连接

image-20251025190350252.png

客户端:SocketChannel , 连接到服务器,读写数据即可

selector和ServerSocketChannel配合使用:实现io多路复用

image-20251025195707176.png

netty

netty对nio进行一层封装,简化了代码的编写,更专注于业务。

image-20251025204155228.png

  • 由专门线程监听连接,接受连接后,将channel分配给worker线程处理,处理流程定义在pipeline中,pipeline中定义各种in或者out的handler

服务端

   // 链式编程:配置服务器端,线程,pipeline,端口
       ServerBootstrap server=new ServerBootstrap()
                .group(new NioEventLoopGroup()) // boss和worker共用一个事件循环组,构造方法默认是1个线程池。group方法有重载,可以分别传入不同的事件循环组
                .channel(NioServerSocketChannel.class) // 配置创建服务端socket类型
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    // childHandler 说明是建立连接后的channel,区别于handler,在客户端用的就是handler
                    protected void initChannel(NioSocketChannel ch) {
                        // 具体业务handler
                        ch.pipeline().addLast(new StringDecoder());
                        ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) {
                                System.out.println(msg);
                            }
                        });
                    }
                });
        ChannelFuture bind = server.bind(8080);
​
​
        ChannelFuture sync = bind.sync(); // 等待服务器启动完成
        sync.addListener(new ChannelFutureListener(){
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (future.isSuccess()) {
                    System.out.println("服务器启动成功");
                } else {
                    System.out.println("服务器启动失败");
                }
            }
        });
        
        Channel channel = sync.channel(); // 获取到对应的ServerSocketChannel,有时候需要获取绑定成功后的channel的一些信息
        System.out.println(channel.localAddress());
        // 添加关闭监听器
        channel.closeFuture().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                // 服务器关闭时执行
                System.out.println("服务器已关闭");
                // 通常在这里关闭EventLoopGroup,以释放资源
                future.channel().eventLoop().shutdownGracefully();
            }
        });
​
  • ServerBootstrap: 服务器启动引导类,配置以及启动 常用设置方法有:group,channel,childHandler optionchildOption:option用于设置NioServerSocketChannel网络设置相关,childOption用于设置客户端连接后对应channel网络设置相关,可以用ChannelOption对应常量设置,每调用一次设置一个网络相关参数。如.option(ChannelOption.SO_BROADCAST, true)表示UDP开启广播模式。
  • NioEventLoopGroup:NIO对应的事件循环组,包含多个NioEventLoop(一个线程),当服务器有事件发生时,交由这些线程处理,group就是一个netty定制化的线程池
  • NioServerSocketChannel: NIO的socketChannel,监听连接请求。还可以配置为OioServerSocketChannel:对应BIO,事件循环组对应改成Oio,BIO中EventLoopGroup的每个线程只能处理一个连接,只要哪个连接没有退出,其他请求无法共享这个线程。EpollServerSocketChannel: 绕过 JDK,直接调用 Linux 的 epoll 系统调用,从而在linux效率比java实现的NIO高一点。
  • ChannelInitializer:Channel初始化器,当新连接建立时,初始化Channel对应的pipeline
  • Pipeline和Handler:Pipeline是一个双向链表结构,里面维护了多个Handler(处理器)。连接后的channel的发生的事件都会按照顺序拦截流经处理链。

ChannelFuture 的特点

  • 非阻塞 + 回调机制:可以添加监听器(GenericFutureListener),在 Future 完成时自动触发回调。间接继承了JDK的future,所以也有其部分特性,也支持获取返回值,不过实际实际返回值是空(void),channel只关注成功与否。

  • netty中每个future几乎都和一个channel相关,每个channel都会绑定到一个EventLoop(即一个线程),和这个channel相关的操作都会在这个线程内完成,包括future的回调。

  • 只读:只能获取结果、检查状态(成功/失败),不能设置结果。和Promise区分,Promise可写的。

  • 常见api:
    sync():直到对应事件执行完毕 addListener():添加一个监听器 isSuccess():是否执行成功

怎么理解Event Loop

  • 事件循环,一个(或少量)线程,通过一个无限循环,不断监听和处理“事件

  • netty中的事件

    Inbound Event(入站事件) :从网络 → 应用(服务器) Outbound Event(出站事件) :从应用(服务器) → 网络

客户端

     NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
        Bootstrap client=new Bootstrap()
                .group(eventExecutors) 
                .channel(NioSocketChannel.class) 
                .handler(new ChannelInitializer<Channel>() { 
                    // 用的是handler
                    @Override
                    protected void initChannel(Channel ch) {
                        ch.pipeline().addLast(new StringEncoder()); 
                    }
                });
​
        ChannelFuture connect = client.connect("127.0.0.1", 8080);
        ChannelFuture sync = connect.sync(); // 阻塞等待连接建立成功
        Channel channel = sync.channel(); // 连接成功后获取channel对象
        channel.writeAndFlush(new Date() + ": hello world!"); // 写入数据并刷新
        channel.closeFuture().addListener(future -> {
            System.out.println("客户端关闭");
        });
        System.out.println(channel.localAddress());
        channel.close();
        eventExecutors.shutdownGracefully(); // 优雅关闭线程池,group内部的线程是非守护线程
  • NioSocketChannel : 对应TCP编程

    NioDatagramChannel : 对应的UDP编程