1、EventLOoop
事件循环对象
EventLoop
本质是一个单线程执行器(同时维护一个Selector),里面有run方法处理Channel上源源不断的事件。
它的继承关系
- 一条线是继承自就j.uc.
ScheduledExecutorService
因此包含了线程池中所有的方法 - 另一条是继承自netty自己的
OrderedEventExecutor
- 提供了
boolean inEventLoop(Thread thread);
方法判断一个线程是否属于此EventLoop - 提供了
parent
()方法来看看自己属于哪个EventExecutorGroup
- 提供了
事件循环组
EventLoopGroup
是一组EventLoop,Channel一般会调用EventLoopGroup
的register方法来绑定其中一个EventLoop
,后续这个Channel上的io事件由此EventLoop
来处理(保证了io事件处理时的线程安全)
- 继承自netty自己的
EventExecutorGroup
- 实现了
Iterable
接口遍历EventLoop
的能力 - 另有next方法获取集合中下一个
EventLoop
- 实现了
💡 方法使用
public static void main(String[] args) { // 1、初级事件循环组 NioEventLoopGroup group = new NioEventLoopGroup(2); //io 事件,普通任务,定时任务 // DefaultEventLoopGroup group1 = new DefaultEventLoopGroup(); //普通任务,定时任务 // 2、获取下一个事件循环对象 会循环获取EventLoopGroup组里的EventLoop执行任务 System.out.println(group.next()); // 3、执行普通任务 group.next().execute(()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("ok"); }); // 4、执行定时任务(继承了ScheduledExecutorService线程池) group.next().scheduleAtFixedRate(()->{ log.debug("ng"); },1,3, TimeUnit.SECONDS); log.debug("main"); }
每一个客户端的请求发送过来时,都会和一个EventLoop进行绑定,绑定后,不会更改,当这个请求发送数据时都会是由这个请求来处理数据
EventLoop使用
- 可以把EventLoop的职责进行细分
public static void main(String[] args) {
new ServerBootstrap()
// 把职责设置得更加明确,分成boss和worker角色,分别处理不同类型事件
// boss负责serverSocketChannel 上的accept事件 worker只负责socketChannel上的读写
.group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override //ByteBuf
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
log.debug(buf.toString(StandardCharsets.UTF_8));
}
});
}
}).bind(8080);
}
📢 handler执行中如何换人(切换执行的EventLoop)?
关键代码 io.netty.channel.AbstractChannelHandlerContext`
通过
inEventLoop
()判断下一个handler中的线程,是否和eventLoop是同一个线程,不是就直接切换为下一个handler的线程
2、Channel
channel的主要作用
- close() 可以用来关闭channel
- closeFuture() 用来处理channel的关闭
- sync() 方法作用是同步等待channel关闭
- addKistener() 方法是异步等待channel关闭
- pipeliner() 方法添加处理器
- write() 方法将数据写入,不会立即刷出,会先存储在缓冲区
- writeAndFlush()方法将数据写入并直接通过网络刷出(发送出去)
ChannelFuture,返回的异步操作结果,因为
Netty中的所有IO操作都是异步的,所以不能够保证结果一定会及时返回;只会返回ChannelFuture
作为异步返回的信息;
ChannelFuture.
sync() 阻塞住当前线程,直到nio线程连接建立完毕才会停止阻塞channelFuture.channel()
获取建立连接的ChannelchannelFuture.addListener
() 异步处理,等待nio线程连接建立后调用该方法- 带有Future、Promise的类型都是和异步方法配套使用,用来处理结果
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()); //把字符串编码成ByteBuffer发送
}
})
// 5、连接到服务器
// 异步非阻塞方法,main 发起调用,真正执行connect 是nio线程
.connect(new InetSocketAddress("localhost", 8080));
/*// 1、同步等待 connect去执行完毕(建立连接)才继续向下运行
channelFuture.sync();
// 无阻塞执行线程
Channel channel = channelFuture.channel();
log.info("----{}",channel);
// 6、向远程服务器发送数据
channel.writeAndFlush("雄安吗 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("雄安吗 world");
}
});
System.out.println("");
}
closeFuture
关闭后需要执行操作;
public static void main(String[] args) throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
ChannelFuture channelFuture = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
ch.pipeline().addLast(new StringEncoder());
}
}).connect(new InetSocketAddress("localhost",8080));
Channel channel = channelFuture.sync().channel();
log.debug("channel对象:{}",channel);
new Thread(()->{
Scanner scanner = new Scanner(System.in);
while (true){
String line = scanner.nextLine();
if ("q".equals(line)){
channel.close();
log.debug("channel对象内{}",channel);
break;
}
channel.writeAndFlush(line);
}
},"input").start();
ChannelFuture closeFuture = channel.closeFuture();
/* log.info("执行关闭");
closeFuture.sync(); //可以保证下面的操作一定是在执行关闭之后--同步执行
log.debug("处理关闭之后的操作");*/
// 异步执行关闭closeFuture后的操作;
closeFuture.addListener((ChannelFutureListener) future-> {
log.debug("处理关闭之后的操作");
// 把group进行优雅的关闭掉
group.shutdownGracefully();
});
}
3、Future & Promise
jdk Future ← netty Future ←promise 三者是继承扩展关系
- jdk Future 只能同步等待任务结束(或成功、或失败)才能得到结果
- netty Future 可以同步等待任务结束得到结果,也可以异步方式得到结果,但都是要等任务结束
- netty Promise不仅有netty Future的功能,而且脱离了任务独立存在,只作为两个线程间传递结果的容器
📢 方法对比:
jdk future
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1、线程池
ExecutorService service = Executors.newFixedThreadPool(2);
// 2、提交任务
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.debug("执行计算");
Thread.sleep(1000);
return 50;
}
});
// 3、主线程通过future 来获取结果
log.debug("等待结果");
log.debug("结果是{}",future.get());
}
Netty Future
public static void main(String[] args) throws ExecutionException, InterruptedException { NioEventLoopGroup group = new NioEventLoopGroup(); EventLoop eventLoop = group.next(); Future<Integer> future = eventLoop.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { log.debug("执行计算"); Thread.sleep(1000); return 70; } }); // 3、主线程通过future 来获取结果 // 同步获取结果 log.debug("等待结果"); // log.debug("结果是{}",future.get()); // 异步获取结果 future.addListener(new GenericFutureListener<Future<? super Integer>>() { @Override public void operationComplete(Future<? super Integer> future) throws Exception { log.debug("接收结果{}",future.getNow()); } }); }
Netty Promise
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1、线程池
ExecutorService service = Executors.newFixedThreadPool(2);
// 2、提交任务
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.debug("执行计算");
Thread.sleep(1000);
return 50;
}
});
// 3、主线程通过future 来获取结果
log.debug("等待结果");
log.debug("结果是{}",future.get());
}