关于netty:netty系列之netty中的Channel详解

4次阅读

共计 4067 个字符,预计需要花费 11 分钟才能阅读完成。

简介

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/

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

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

正文完
 0