简介

Channel是连贯ByteBuf和Event的桥梁,netty中的Channel提供了对立的API,通过这种对立的API,netty能够轻松的对接多种传输类型,如OIO,NIO等。明天本文将会介绍Channel的应用和Channel相干的一些概念。

Channel详解

Channel是什么? Channel是一个连贯网络输出和IO解决的桥梁。你能够通过Channel来判断以后的状态,是open还是connected,还能够判断以后Channel反对的IO操作,还能够应用ChannelPipeline对Channel中的音讯进行解决。

先看下Channel的定义:

public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparable<Channel> {

能够看到Channel是一个接口,它继承了AttributeMap, ChannelOutboundInvoker, Comparable三个类。Comparable示意这个类能够用来做比拟。AttributeMap用来存储Channel的各种属性。ChannelOutboundInvoker次要负责Channel和内部 SocketAddress 进行连贯和对写。

再看下channel中定义的办法:

能够看出channel中定义的办法是多种多样的,这些办法都有些什么特点呢?接下来一一为您解说。

异步IO和ChannelFuture

netty中所有的IO都是异步IO,也就是说所有的IO都是立刻返回的,返回的时候,IO可能还没有完结,所以须要返回一个ChannelFuture,当IO有后果之后,会去告诉ChannelFuture,这样就能够取出后果了。

ChannelFuture是java.util.concurrent.Future的子类,它除了能够拿到线程的执行后果之外,还对其进行了扩大,退出了当前任务状态判断、期待工作执行和增加listener的性能。

其余的性能都很好了解,它的冲破在于能够对ChannelFuture增加listener,咱们列出一个增加listener的办法:

Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);

增加的Listener会在future执行完结之后,被告诉。不须要本人再去调用get期待future完结。这里实际上就是异步IO概念的实现,不须要被动去调用,当你实现之后来告诉我就行。十分的美妙!

ChannelFuture 有两个状态:uncompleted或者completed,别离代表工作的执行状态。

当一个IO刚开始的时候,返回一个ChannelFuture对象,这个对象的初始状态是uncompleted。留神,这个状态下的IO是还未开始工作的状态。当IO实现之后,不论是succeeded, failed 或者 cancelled状态,ChannelFuture的状态都会转换成为completed。

下图展现的是ChannelFuture状态和IO状态的对应图:

                                    +---------------------------+                                    | Completed successfully    |                                    +---------------------------+                               +---->      isDone() = true      |

+--------------------------+ | | isSuccess() = true |
| Uncompleted | | +===========================+
+--------------------------+ | | Completed with failure |
| isDone() = false | | +---------------------------+
| isSuccess() = false |----+----> isDone() = true |
| isCancelled() = false | | | cause() = non-null |
| cause() = null | | +===========================+
+--------------------------+ | | Completed by cancellation |

                               |    +---------------------------+                               +---->      isDone() = true      |                                    | isCancelled() = true      |                                    +---------------------------+

如果要监控IO的状态,能够应用下面咱们提到的 addListener 办法,为ChannelFuture增加一个ChannelFutureListener。

如果要期待IO执行结束,还有一个await()办法,然而这个办法会去期待IO执行结束,是一个同步的办法,所以并不举荐。

相比而言,addListener(GenericFutureListener)是一个非阻塞的异步办法,将会把一个ChannelFutureListener增加到ChannelFuture中,当IO完结之后会主动告诉ChannelFutureListener,十分好用。

对于解决IO操作的ChannelHandler来说,为了防止IO的阻塞,肯定不要在ChannelHandler的IO办法中调用await(),这样有可能会导致ChannelHandler因为IO阻塞导致性能降落。

上面举两个例子,一个是谬误的操作,一个是正确的操作:

   // 错误操作    @Override   public void channelRead(ChannelHandlerContext ctx, Object msg) {       ChannelFuture future = ctx.channel().close();       future.awaitUninterruptibly();       // 调用其余逻辑   }     // 正确操作    @Override   public void channelRead(ChannelHandlerContext ctx, Object msg) {       ChannelFuture future = ctx.channel().close();       future.addListener(new ChannelFutureListener() {           public void operationComplete(ChannelFuture future) {               // 调用其余逻辑           }       });   }

大家能够比照下下面两种写法的区别。

另外要留神的是ChannelFuture中的这些await办法比方:await(long), await(long, TimeUnit), awaitUninterruptibly(long), 或者 awaitUninterruptibly(long, TimeUnit)能够带一个过期工夫,大家要留神的是这个过期工夫是期待IO执行的工夫,并不是IO的timeout工夫,也就是说当await超时之后,IO还有可能没有执行实现,这就导致了上面的代码有可能报错:

   Bootstrap b = ...;   ChannelFuture f = b.connect(...);   f.awaitUninterruptibly(10, TimeUnit.SECONDS);   if (f.isCancelled()) {       // 用户勾销了Channel   } else if (!f.isSuccess()) {       // 这里可能会报异样,因为底层的IO可能还没有执行实现       f.cause().printStackTrace();   } else {       // 胜利建设连贯   }  

下面的代码能够改成上面的例子:

  Bootstrap b = ...;   // 配置连贯timeout的工夫   b.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);   ChannelFuture f = b.connect(...);   f.awaitUninterruptibly();     // 期待直到底层IO执行结束   assert f.isDone();     if (f.isCancelled()) {       // 用户手动勾销Channel   } else if (!f.isSuccess()) {       f.cause().printStackTrace();   } else {       // 胜利建设连贯   }   

Channel的层级构造

netty中的Channel是有层级构造的,通过parent属性可获取这种层级构造。parent获取的对象和Channel的创立形式无关。比方如果是一个被ServerSocketChannel accepted的SocketChannel,那么它的parent就是ServerSocketChannel。

开释资源

和所有的IO一样,Channel在用完之后也须要被开释,须要调用close()或者close(ChannelPromise) 办法。

事件处理

channel负责建设连贯,建设好的连贯就能够用来处理事件ChannelEvent了,实际上ChannelEvent是由定义的一个个Channelhandler来解决的。而ChannelPipeline就是连贯channel和channelhandler的桥梁。

咱们将会下下一章具体解说ChannelEvent、Channelhandler和ChannelPipeline的关联关系,敬请期待。

总结

Channel在netty中是做为一个要害的通道而存在的,前面的Event和Handler是以channel为根底运行的,所以说Channel就是netty的根底,好了,明天的介绍到这里就完结了,敬请期待后续的文章。

本文已收录于 http://www.flydean.com/04-netty-channel/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」,懂技术,更懂你!