Netty学习笔记

本文是关于Netty的学习笔记,涵盖了Netty的入门知识,如依赖、案例和组件,深入讲解了EventLoop、Channel、Future/Promise、Handler/Pipeline以及ByteBuf的原理和使用。此外,还探讨了Netty的进阶主题,包括粘包与半包、协议解析、聊天室案例优化和参数调优,并简要分析了源码相关流程。
摘要由CSDN通过智能技术生成

入门

Netty 是一个异步的(多线程)、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端

依赖

<dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.39.Final</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
        <!--专门做json转换的-->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>
        <!--工具类合集-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>19.0</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

案例

image-20220121115640656

// 客户端
public static void main(String[] args) throws InterruptedException {
   
    // 1.启动类
    new Bootstrap()
            // 2.添加 EventLoop
            .group(new NioEventLoopGroup())
            // 3.选择客户端 channel 实现
            .channel(NioSocketChannel.class)
            // 4. 添加handler
            .handler(new ChannelInitializer<NioSocketChannel>() {
   
                @Override // 在建立连接后被调用
                protected void initChannel(NioSocketChannel ch) throws Exception {
   
                    ch.pipeline().addLast(new StringEncoder());
                }
            })
            // 5.连接到服务器
            .connect(new InetSocketAddress("localhost",8080))
            .sync()
            .channel()
            // 6.发送数据
            .writeAndFlush("hello,world");
}
// 服务器
public static void main(String[] args) {
   
    // 1. 服务器端启动器,负责组装netty组件
    new ServerBootstrap()
            // 2. 组
            .group(new NioEventLoopGroup())
            // 3. 选择服务器的实现方式 nio oio ...
            .channel(NioServerSocketChannel.class)
            .childHandler(
                    // 4. 初始化channel并添加 handler ,决定处理事件类型
                    new ChannelInitializer<NioSocketChannel>() {
   
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
   
                    // 5. 添加具体 handler
                    // 将 ByteBuf 转为 字符串
                    ch.pipeline().addLast(new StringDecoder());
                    // 自定义 handler
                    ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
   
                        @Override // 读事件
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
                            // 打印输出
                            System.out.println(msg);
                        }
                    });
                }
            })
            .bind(8080);
}

组件

EventLoop
事件循环对象

EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件

image-20220121122312308
事件循环组

EventLoopGroup 是一组EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法 来绑定其中一个 EventLoop,后续这个 Channel 上的 io事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)

image-20220121122502687
多个 handler

当有的任务需要较长的时间处理时,可以使用非NioEventLoopGroup,避免同一个NioEventLoop中的其他Channel在较长的时间内都无法得到处理

public class MyServer {
   
    public static void main(String[] args) {
   
        // 增加自定义的非NioEventLoopGroup
        EventLoopGroup group = new DefaultEventLoopGroup();
        
        new ServerBootstrap()
                .group(new NioEventLoopGroup(1), new NioEventLoopGroup(2))
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
                        // 增加两个handler,第一个使用NioEventLoopGroup处理,第二个使用自定义EventLoopGroup处理
                        socketChannel.pipeline().addLast("nioHandler",new ChannelInboundHandlerAdapter() {
   
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));
                                // 发送消息至下一个handler
                                ctx.fireChannelRead(msg);
                            }
                        })
                        // 该handler绑定自定义的Group
                        .addLast(group, "myHandler", new ChannelInboundHandlerAdapter() {
   
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
   
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(Thread.currentThread().getName() + " " + buf.toString(StandardCharsets.UTF_8));
                            }
                        });
                    }
                })
                .bind(8080);
    }
}

img

Channel

channel 的主要作用

  • close( ) 关闭 channel
  • closeFuture( ) 处理 channel 的关闭
    • sync 方法作用是同步等待 channel 关闭
    • addListener 方法是异步等待 channel 关闭
  • pipeline( ) 方法添加处理器
  • write( ) 方法将数据写入
  • writeAndFlush( ) 方法将数据写入并刷出(立刻)
处理连接
方式一:主线程处理
public class MyClient {
   
    public static void main(String[] args) throws IOException, InterruptedException {
   
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
                        socketChannel.pipeline().addLast(new StringEncoder());
                    }
                })
                // 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程
            	// NIO线程:NioEventLoop 中的线程
                .connect(new InetSocketAddress("localhost", 8080));
        
        // 该方法用于等待连接真正建立
        channelFuture.sync();
        
        // 获取客户端-服务器之间的Channel对象
        Channel channel = channelFuture.channel();
        channel.writeAndFlush("hello world");
        System.in.read();
    }
}
方式二:监听器处理
public class MyClient {
   
    public static void main(String[] args) throws IOException, InterruptedException {
   
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
                        socketChannel.pipeline().addLast(new StringEncoder());
                    }
                })
                // 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程
                // NIO线程:NioEventLoop 中的线程
                .connect(new InetSocketAddress("localhost", 8080));
        
		// 当connect方法执行完毕后,也就是连接真正建立后
        // 会在NIO线程中调用operationComplete方法
        channelFuture.addListener(new ChannelFutureListener() {
   
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
   
                Channel channel = channelFuture.channel();
                channel.writeAndFlush("hello world");
            }
        });
        System.in.read();
    }
}
处理关闭
方式一:主线程处理

public class ReadClient {
   
    public static void main(String[] args) throws InterruptedException {
   
        // 创建EventLoopGroup,使用完毕后关闭
        NioEventLoopGroup group = new NioEventLoopGroup();
        
        ChannelFuture channelFuture = new Bootstrap()
                .group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
   
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
   
                        socketChannel.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8080));
        channelFuture.sync();

        Channel channel = channelFuture.channel();
        Scanner scanner = new Scanner(System.in);

        // 创建一个线程用于输入并向服务器发送
        new Thread(()->{
   
            while (true) {
   
                String msg = scanner.next();
                if ("q".equals(msg)) {
   
                    // 关闭操作是异步的,在NIO线程中执行
                    channel.close();
                    break;
                }
                channel.writeAndFlush(msg);
            }
        }, "inputThread").start();

        // 获得closeFuture对象
        ChannelFuture closeFuture = channel.closeFuture();
        System.out.println("waiting close...");
        
        // 同步等待NIO线程执行完close操作
        closeFuture.sync();
        
        // 关闭之后执行一些操作,可以保证执行的操作一定是在channel关闭以后执行的
        System.out.println("关闭之后执行一些额外操作...");
        
        // 关闭EventLoopGroup
        group.shutdownGracefully();
    }
}
方式二:通过listener使nio线程处理关闭
// ...
// 获得closeFuture对象
ChannelFuture closeFuture = channel.closeFuture();

// 同步等待NIO线程执行完close操作
closeFuture.sync();
closeFuture.addListener(new ChannelFutureListener() {
   
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
   
        // 等待channel关闭后才执行的操作
        System.out.println("关闭之后执行一些额外操作...");
        // 关闭EventLoopGroup
        group.shutdownGracefully();
    }
});
Future & Promise

netty 中的 Future 与 jdk 中的 Future 同名,但是是两个接口

netty 的 Future 继承自 jdk 的 Future,而 Promise 又对 netty Future 进行了扩展

  • jdk Future 只能同步等待任务结束(或成功、或失败)才能得到结果
  • netty Future 可以同步等待任务结束得到结果,也可以异步方式得到结果,但都是要等任务结束
  • netty Promise 不仅有 netty Future 的功能,而且脱离了任务独立存在,只作为两个线程间传递结果的容器
image-20220121204859635

image-20220121204817946

JDK Future
public class JdkFuture {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        ThreadFactory factory = new ThreadFactory() {
   
            @Override
            public Thread newThread(Runnable r) {
   
                return new Thread(r, "JdkFuture");
            }
        };
        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10,10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10), factory);

        // 获得Future对象
        Future<Integer> future = executor.submit(new Callable<Integer>() {
   

            @Override
            public Integer call() throws Exception {
   
                TimeUnit.SECONDS.sleep(1);
                return 50;
            }
        });

        // 通过阻塞的方式,获得运行结果
        System.out.println(future.get());
    }
}
Netty Future
public class NettyFuture {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        NioEventLoopGroup group = new NioEventLoopGroup();

        // 获得 EventLoop 对象
        EventLoop eventLoop = group.next();
        Future<Integer> future = eventLoop.submit(new Callable<Integer>() {
   
            @Override
            public Integer call() throws Exception {
   
                return 50;
            }
        });

        // 主线程中获取结果
        System.out.println(Thread.currentThread().getName() + " 获取结果");
        System.out.println("getNow " + future.getNow());
        System.out.println("get " + future.get());

        // NIO线程中异步获取结果
        future.addListener(new GenericFutureListener<Future<? super Integer>>() {
   
            @Override
            public void operationComplete(Future<? super Integer> future) throws Exception {
   
                System
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值