channel的常用方法
- close() 用于关闭channel
- closeFuture() 用来处理channel关闭后的相关操作(所谓的优雅关闭)
- sync() 让启动异步操作线程等待异步线程完成之后在操作
- addListener() 用来把操作交给另外一个线程,这个线程会等待异步线程操作完之后再进行操作
- pipeline() 用于添加处理器 handler
- wirte() 向客户点写入数据,但是只是写入缓冲区,不会立即发送到服务器端,只有保存到一定程度了或者客户端调用了flush()方法的时候,才会发送
- writeAndFlush() 写入并且发送
ChannelFuture
在客户端代码的编写中,可以看到
在创建完连接后,调用了一个叫sync()的方法,现在来说明一下为什么要调用这个方法。首先connect这个方法是异步的,也就是主线程会把连接的工作交给一个子线程去操作,所以如果主线程不等待连接成功之后,直接调用channel去写数据,就会出现连接没有完成,但是数据已经写出去的情况,为了解决这样的问题,一般采用两种方式:
- 使用sync()方法,让线程阻塞到结果执行完成之后再进行后续操作
- 使用Future的 addListener方法,交给另外一个线程去等待结果并执行后续操作
使用addListener的方法简单代码:
package com.test.netty.c2;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
@Slf4j
public class EventLoopClient {
public static void main(String[] args) throws Exception {
//1、启动类 带有Future、Promise都是异步方法配套使用,来处理结果
ChannelFuture channelFuture = new Bootstrap()
//2、添加EventLoop
.group(new NioEventLoopGroup())
//3、选择客户端channel实现
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
//建立连接后调用
channel.pipeline().addLast(new StringEncoder());
}
})
//5、连接到服务器
//异步非阻塞,调用connect方法的是主线程,因为是异步的,所以真正执行连接的是nio线程
//nio 连接很慢,所以不调用 channelFuture.sync(); 进行阻塞,写数据会比连接快,服务器端就不会接受到数据
.connect(new InetSocketAddress("127.0.0.1", 8080));
//2.1同步方法,同步处理结果,调用线程会阻塞住,等到nio连接处理好了,就会向下运行
//channelFuture.sync();
// Channel channel = channelFuture.channel();
// channel.writeAndFlush("112");
// System.in.read();
//2.2 使用addListener(回调对象) 异步处理结果
channelFuture.addListener((ChannelFutureListener) future -> {
//在nio建立完成连接后,进行调用
Channel channel = future.channel();
log.debug("channel:{}", channel);
channel.writeAndFlush("112");
});
}
}
其实对于channel的关闭也是一个道理,下面是简单例子:
package com.test.netty.c2;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.Scanner;
@Slf4j
public class CloseChannelFuture {
public static void main(String[] args) throws Exception {
NioEventLoopGroup group = new NioEventLoopGroup();
ChannelFuture channelFuture = new Bootstrap()
//2、添加EventLoop
.group(group)
//3、选择客户端channel实现
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
//建立连接后调用
channel.pipeline().addLast(new StringEncoder());
}
})
//5、连接到服务器
//异步非阻塞,调用connect方法的是主线程,因为是异步的,所以真正执行连接的是nio线程
//nio 连接很慢,所以不调用 channelFuture.sync(); 进行阻塞,写数据会比连接快,服务器端就不会接受到数据
.connect(new InetSocketAddress("127.0.0.1", 8080));
Channel channel = channelFuture.sync().channel();
new Thread(()->{
Scanner scanner = new Scanner(System.in);
while(true){
String line = scanner.nextLine();
if("q".equals(line)){
channel.close();//异步操作,是其他线程进行关闭
break;
}
channel.writeAndFlush(line);
}
}, "input").start();
//获取 CloseFuture 1、同步模式处理关闭
ChannelFuture closeFuture = channel.closeFuture();
// closeFuture.sync();
// log.debug("关闭之后的操作");
// 2、异步模式处理关闭
closeFuture.addListener((ChannelFutureListener) future -> {
log.debug("关闭之后的操作");
//优雅的关闭
group.shutdownGracefully();
});
//netty 异步提高的是 单位时间内的吞吐量,而不是响应时间
}
}
为什么使用异步,总结一句话,异步并不是提高响应时间,甚至增加了响应时间,提高的是单位时间内的吞吐量。本文章并非强调channel怎么使用,而是记录了在netty中,异步的使用方式,当一个threadA调用了异步方法,其实就是把某些操作交给了另外一个threadB进行操作,如果想在threadA中等到threadB中操作的结果,可以使用之前讲过的两种方式。