当前位置: 首页 > 后端技术 > Java

Netty系列:netty中Channel详解

时间:2023-04-01 14:55:15 Java

介绍Channel是连接ByteBuf和Event的桥梁。netty中的Channel提供了统一的API。通过这个统一的API,netty可以方便的连接多种传输类型,比如OIO、NIO等。今天这篇文章就来介绍一下Channel的使用以及Channel相关的一些概念。频道详解什么是频道?Channel是连接网络输入和IO处理的桥梁。可以通过Channel判断当前状态,是打开还是连接,也可以判断当前Channel支持的IO操作,也可以使用ChannelPipeline来处理Channel中的消息。首先看Channel的定义:publicinterfaceChannelextendsAttributeMap,ChannelOutboundInvoker,Comparable{可以看到Channel是一个接口,继承了AttributeMap,ChannelOutboundInvoker,Comparable三个类。Comparable表示这个类可以用来做比较。AttributeMap用于存储Channel的各种属性。ChannelOutboundInvoker主要负责Channel与外部SocketAddress的连接和写入。再看一下通道中定义的方法:可以看出通道中定义了各种方法。这些方法有什么特点?让我一一为您解释。asynchronousIO和ChannelFuturenetty中的所有IO都是异步IO,也就是说所有的IO都是立即返回的。返回的时候,IO可能还没有结束,所以需要返回一个ChannelFuture。当IO有结果时,会通知ChannelFuture,这样就可以取回结果了。ChannelFuture是java.util.concurrent.Future的子类。除了获取线程的执行结果外,还对其进行了扩展,增加了判断当前任务状态、等待任务执行、增加监听等功能。其他功能很好理解。它的突破在于能够为ChannelFuture添加监听器。我们列出一个添加监听器的方法:FutureaddListeners(GenericFutureListener>...listeners);添加的Listener会在以后执行结束后得到通知。无需调用get等待future结束。这其实就是异步IO概念的实现。你不需要主动调用它,完成后通知我即可。很漂亮!ChannelFuture有两种状态:未完成或完成,分别代表任务的执行状态。当一个IO刚开始时,返回一个ChannelFuture对象,这个对象的初始状态是未完成。注意这个状态的IO是还没有开始工作的状态。当IO完成后,无论是成功、失败还是取消,ChannelFuture的状态都会转换为completed。下图是ChannelFuture状态和IO状态的对应关系:+----------------------------+|成功完成|+------------------------++---->isDone()=true|+-------------------------+||是成功()=真||未完成||+============================++--------------------------+||失败完成||完成()=假||+--------------------------+|isSuccess()=false|----+---->isDone()=true||isCancelled()=假|||原因()=非空||原因()=空||+=============================++----------------------+||通过取消完成||+----------------------------++---->isD一个()=真||isCancelled()=真|+----------------------------+如果要监听IO的状态,可以使用我们上面提到的addListener方法来添加ChannelFuture的ChannelFutureListener。如果要等待IO执行完,还有一个await()方法,但是这个方法会等待IO执行完。它是一种同步方法,因此不推荐使用。相比之下,addListener(GenericFutureListener)是一个非阻塞的异步方法,会在ChannelFuture中添加一个ChannelFutureListener,当IO结束时会自动通知ChannelFutureListener,非常好用。对于处理IO操作的ChannelHandler,为了避免IO阻塞,一定不要在ChannelHandler的IO方法中调用await(),这样可能会因为IO阻塞导致ChannelHandler性能下降。下面举两个例子,一个是错误操作,一个是正确操作://错误操作@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ChannelFuturefuture=ctx.channel().close();未来。等待不可中断();//调用其他逻辑}//正确操作@OverridepublicvoidchannelRead(ChannelHandlerContextctx,Objectmsg){ChannelFuturefuture=ctx.channel().close();future.addListener(newChannelFutureListener(){publicvoidoperationComplete(ChannelFuturefuture){//调用其他逻辑}});}大家可以对比一下上面两种写法的区别。另外需要注意的是ChannelFuture中的这些await方法,比如:await(long),await(long,TimeUnit),awaitUninterruptibly(long),或者awaitUninterruptibly(long,TimeUnit)都可以有过期时间,大家要注意这个过期时间是等待IO执行的时间,不是IO的超时时间。也就是说,await超时后,IO可能还没有执行,这可能会导致如下代码报错:Bootstrapb=...;ChannelFuturef=b.connect(...);f.awaitUninterruptibly(10,TimeUnit.SECONDS);if(f.isCancelled()){//用户取消了Channel}elseif(!f.isSuccess()){//这里可能会报异常,因为底层IO可能还没有执行f.cause().printStackTrace();}else{//成功建立连接}上面的代码可以改成下面的例子:Bootstrapb=...;//配置连接超时时间b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,10000);ChannelFuturef=b.connect(...);f.awaitUninterruptibly();//等待底层IO执行完毕assertf.isDone();if(f.isCancelled()){//用户手动取消了Channel}elseif(!f.isSuccess()){f.cause().printStackTrace();}else{//连接建立成功}Channelnetty中的Channel是有层次结构的,可以通过parent属性获取。parent获得的对象与Channel的创建方式有关。例如,如果它是一个被ServerSocketChannel接受的SocketChannel,那么它的parent就是ServerSocketChannel。释放资源和所有IO一样,Channel用完后也需要释放,需要调用close()或close(ChannelPromise)方法。事件处理通道负责建立连接,建立的连接可以用来处理事件ChannelEvent。实际上ChannelEvent是由定义的ChannelHandler处理的。而ChannelPipeline是连接channel和channelhandler的桥梁。我们将在下一章详细讲解ChannelEvent、ChannelHandler和ChannelPipeline的关系,敬请期待。综上所述,Channel作为netty中的关键通道存在。下面的Events和Handlers都是在channel的基础上进行操作的,所以Channel是netty的基础。好了,今天的介绍就到这里了,敬请期待后续文章。本文已收录于http://www.flydean.com/04-netty-channel/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等着你去探索!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!