Netty学习(四)-- Channel

1、Channel

Channel 的主要作用:

  • close() 可以用来关闭 Channel
  • closeFuture() 可以用来处理 Channel 关闭
    • sync() 方法作用是同步等待 Channel 关闭
    • addListener() 方法是异步等待 Channel 关闭
  • pipeline() 方法添加处理器
  • write() 方法将数据写入
  • flush() 方法将数据刷出
  • writeAndFlush() 方法将数据写入并刷出

带有 FuturePromise 的类型都是和 异步方法配套使用,用来处理结果

1)ChannelFuture - 连接问题

调用 connect() 方法建立连接,该方法是异步非阻塞的,执行该方法的是 NIO 的线程,main 线程代码会继续向下运行。而建立连接需要一定的时间,继续向下执行的获取 Channel 和 发送数据的代码就不符合逻辑了。

@Slf4j
public static void main(String[] args) throws InterruptedException {
    
    
    ChannelFuture channelFuture = new Bootstrap()
        .group(new NioEventLoopGroup())
        .channel(NioSocketChannel.class)
        .handler(new ChannelInitializer<NioSocketChannel>() {
    
    
            @Override   // 连接建立后被调用
            protected void initChannel(NioSocketChannel ch) throws Exception {
    
    
                ch.pipeline().addLast(new StringEncoder());
            }
        })
        // 1、连接到服务器
        // connect 方法是异步非阻塞。main 发起了调用,真正执行 connect 是 NIO 线程
        .connect(new InetSocketAddress("localhost", 8888));   // 建立连接需要一定的时间,非阻塞就直接向下运行
    // 将 sync() 注释掉,服务器没接收到 消息
    // channelFuture.sync();
    // 无阻塞的向下执行 channel(),
    Channel channel = channelFuture.channel();
    log.info("{}", channel);
    // 2、向服务器发送数据
    channel.writeAndFlush("Hello World!");
}
2)ChannelFuture - 连接问题解决

1、使用 sync() 方法来同步处理结果

channelFuture.sync();    // 阻塞住当前线程,直到 NIO 线程建立完毕
// 无阻塞的向下执行 channel(),
Channel channel = channelFuture.channel();
log.info("{}", channel);
// 向服务器发送数据
channel.writeAndFlush("Hello World!");

2、使用 addListener(回调对象) 方法来同步处理结果

channelFuture.addListener(new ChannelFutureListener() {
    
    
    @Override   // 在 NIO 线程连接建立好了,会调用 operationComplete
    public void operationComplete(ChannelFuture future) throws Exception {
    
    
        Channel channel = future.channel();
        log.info("{}", channel);
        channel.writeAndFlush("Hello World!");
    }
});
3)ChannelFuture - 关闭问题

问题

....
// 2、向服务器发送数据
Channel channel = channelFuture.sync().channel();
log.debug("{}", channel);
new Thread(()->{
    
    
    Scanner scanner = new Scanner(System.in);
    while (true){
    
    
        String line = scanner.nextLine();
        if ("q".equals(line)){
    
    
            channel.close();    // 异步操作,需要一定的时间
            log.debug("处理关闭之后的操作...");		// 不能在这里善后
            break;
        }
        channel.writeAndFlush(line);
    }
}, "input").start();
// log.debug("处理关闭之后的操作..."); 	// 不能在这里善后

使用 Netty 的日志调试

加上 LoggingHandler :

.handler(new ChannelInitializer<NioSocketChannel>() {
    
    
                    @Override   // 连接建立后被调用
                    protected void initChannel(NioSocketChannel ch) throws Exception {
    
    
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG)); // <=====
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })

log4j2.xml 文件加上以下配置,配个 Lombox 的注解 @Slf4j 注解,就可以使用日志打印了:

<loggers>
    ...
    <logger name="io.netty.handler.logging.LoggingHandler" level="DEBUG" additivity="false">
        <appender-ref ref="Console" />
    </logger>
    ...
</loggers>

在这里插入图片描述

4)ChannelFuture - 关闭问题解决

获取 CloseFuture 对象

1、同步处理关闭

// 获取 CloseFuture 对象
ChannelFuture closeFuture = channel.closeFuture();
log.debug("waiting close....");
// 调用 channel.close(); 完成后才执行
closeFuture.sync();
log.debug("处理关闭之后的操作...");

2、异步处理关闭

// 2、异步处理 (和调用 close 方法的是同一个线程)
closeFuture.addListener(new ChannelFutureListener() {
    
    
    @Override
    public void operationComplete(ChannelFuture future) throws Exception {
    
    
        log.debug("处理关闭之后的操作...");
    }
});

NioEventLoopGroup 关闭连接时的处理

需要将 group 提取变量

NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();

在关闭之后调用一下方法:

// 在一段时间内没有新的任务提交就关闭
nioEventLoopGroup.shutdownGracefully();
5)为什么也要使用异步呢?

思考下面的场景,4 个医生给人看病,每个病人花费 20 分钟,而且医生看病的过程中是以病人为单位的,一个病人看完了,才能看下一个病人。假设病人源源不断地来,可以计算一下 4 个医生一天工作 8 小时,处理的病人总数是:4 * 8 * 3 = 96

在这里插入图片描述

经研究发现,看病可以细分为四个步骤,经拆分后每个步骤需要 5 分钟,如下

在这里插入图片描述

因此可以做如下优化,只有一开始,医生 2、3、4 分别要等待 5、10、15 分钟才能执行工作,但只要后续病人源源不断地来,他们就能够满负荷工作,并且处理病人的能力提高到了 4 * 8 * 12 效率几乎是原来的四倍

在这里插入图片描述

要点

  • 单线程没法异步提高效率,必须配合多线程、多核 cpu 才能发挥异步的优势
  • 异步并没有缩短响应时间,反而有所增加
  • 合理进行任务拆分,也是利用异步的关键

没有提升总效率,提高的是单线程的吞吐量(单位时间内能够处理的请求数)。

猜你喜欢

转载自blog.csdn.net/weixin_43989102/article/details/126736367