首页 > 代码库 > netty入门01

netty入门01

目录(?)[-]

  1. Netty4
  2. Channel
  3. ChannelHandler
  4. ChannelPipeline
  5. ChannelHandlerContext
  6. ChannelPipeline和ChannelHandlerContext默认实现
  7. ChannelHandler的种类
  8. 事件的传播
  9. 参考资料

Netty4

Netty是一个和MINA类似的Java NIO框架,目前的最新版本是4.0.13,这两个框架的主要作者好像都是同一个韩国人。


Channel

Channel是Netty最核心的接口,一个Channel就是一个联络Socket的通道,通过Channel,你可以对Socket进行各种操作。


ChannelHandler

用Netty编写网络程序的时候,你很少直接操纵Channel,而是通过ChannelHandler来间接操纵Channel。


ChannelPipeline

ChannelPipeline实际上应该叫做ChannelHandlerPipeline,可以把ChannelPipeline看成是一个ChandlerHandler的链表,当需要对Channel进行某种处理的时候,Pipeline负责依次调用每一个Handler进行处理。每个Channel都有一个属于自己的Pipeline,调用Channel#pipeline()方法可以获得Channel的Pipeline,调用Pipeline#channel()方法可以获得Pipeline的Channel。

技术分享

ChannelPipeline的方法有很多,其中一部分是用来管理ChannelHandler的,如下面这些:

[java] view plain copy 技术分享技术分享
  1. ChannelPipeline addFirst(String name, ChannelHandler handler);  
  2. ChannelPipeline addLast(String name, ChannelHandler handler);  
  3. ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);  
  4. ChannelPipeline addAfter(String baseName, String name, ChannelHandler handler);  
  5. ChannelPipeline remove(ChannelHandler handler);  
  6. ChannelHandler remove(String name);  
  7. ChannelHandler removeFirst();  
  8. ChannelHandler removeLast();  
  9. ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);  
  10. ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);  
  11. ChannelHandler first();  
  12. ChannelHandler last();  
  13. ChannelHandler get(String name);  

根据上面的方法,能够大概想象的到Pipeline按照什么样的方式组织Handler。

ChannelHandlerContext

ChannelPipeline并不是直接管理ChannelHandler,而是通过ChannelHandlerContext来间接管理,这一点通过ChannelPipeline的默认实现DefaultChannelPipeline可以看出来。

技术分享

调用ChannelHandlerContext#channel()方法可以得到和Context绑定的Channel,调用ChannelHandlerContext#handler()方法可以得到和Context绑定的Handler。


ChannelPipeline和ChannelHandlerContext默认实现

DefaultChannelHandlerContext和DefaultChannelPipeline是ChannelHandlerContext和ChannelPipeline的默认实现,下面是它们的部分代码:

[java] view plain copy 技术分享技术分享
  1. final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext {  
  2.   
  3.     volatile DefaultChannelHandlerContext next;  
  4.     volatile DefaultChannelHandlerContext prev;  
  5.   
  6.     private final boolean inbound;  
  7.     private final boolean outbound;  
  8.     private final AbstractChannel channel;  
  9.     private final DefaultChannelPipeline pipeline;  
  10.     private final String name;  
  11.     private final ChannelHandler handler;  
  12.     private boolean removed;  
  13.   
  14.     // ...  
  15. }  
[java] view plain copy 技术分享技术分享
  1. final class DefaultChannelPipeline implements ChannelPipeline {  
  2.     // ...  
  3.   
  4.     final DefaultChannelHandlerContext head;  
  5.     final DefaultChannelHandlerContext tail;  
  6.   
  7.     // ...  
  8. }  


从上面的代码可以看出,在DefaultPipeline内部,DefaultChannelHandlerContext组成了一个双向链表:

技术分享

再来看看DefaultChannelPipeline的构造函数:

[java] view plain copy 技术分享技术分享
  1. public DefaultChannelPipeline(AbstractChannel channel) {  
  2.     if (channel == null) {  
  3.         throw new NullPointerException("channel");  
  4.     }  
  5.     this.channel = channel;  
  6.   
  7.     TailHandler tailHandler = new TailHandler();  
  8.     tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);  
  9.   
  10.     HeadHandler headHandler = new HeadHandler(channel.unsafe());  
  11.     head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);  
  12.   
  13.     head.next = tail;  
  14.     tail.prev = head;  
  15. }  


可以看到,DefaultChinnelPipeline内部使用了两个特殊的Handler来表示Handler链的头和尾:

技术分享

ChannelHandler的种类

从上面DefaultChannelHandlerContext代码可以知道,Handler实际上分为两种,Inbound和Outbound,这一点也可以从ChannelHandler接口的子接口得到证明:

[java] view plain copy 技术分享技术分享
  1. public interface ChannelInboundHandler extends ChannelHandler {  
  2.   // ...  
  3. }  
  4.   
  5. public interface ChannelOutboundHandler extends ChannelHandler {  
  6.   // ...  
  7. }  

 

事件的传播

为了搞清楚事件如何在Pipeline里传播,让我们从Channel的抽象子类AbstractChannel开始,下面是AbstractChannel#write()方法的实现:

[java] view plain copy 技术分享技术分享
  1. public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {  
  2.     // ...  
  3.     @Override  
  4.     public Channel write(Object msg) {  
  5.         return pipeline.write(msg);  
  6.     }  
  7.     // ...  
  8. }  


AbstractChannel直接调用了Pipeline的write()方法:

技术分享

再看DefaultChannelPipeline的write()方法实现:

[java] view plain copy 技术分享技术分享
  1. final class DefaultChannelPipeline implements ChannelPipeline {  
  2.     // ...  
  3.     @Override  
  4.     public ChannelFuture write(Object msg) {  
  5.         return tail.write(msg);  
  6.     }  
  7.     // ...  
  8. }  


因为write是个outbound事件,所以DefaultChannelPipeline直接找到tail部分的context,调用其write()方法:

技术分享

接着看DefaultChannelHandlerContext的write()方法:

[java] view plain copy 技术分享技术分享
  1. final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext {  
  2.     // ...  
  3.     @Override  
  4.     public ChannelFuture write(Object msg) {  
  5.         return write(msg, newPromise());  
  6.     }  
  7.   
  8.     @Override  
  9.     public ChannelFuture write(final Object msg, final ChannelPromise promise) {  
  10.         if (msg == null) {  
  11.             throw new NullPointerException("msg");  
  12.         }  
  13.   
  14.         validatePromise(promise, true);  
  15.   
  16.         write(msg, false, promise);  
  17.   
  18.         return promise;  
  19.     }  
  20.   
  21.     private void write(Object msg, boolean flush, ChannelPromise promise) {  
  22.         DefaultChannelHandlerContext next = findContextOutbound();  
  23.         next.invokeWrite(msg, promise);  
  24.         if (flush) {  
  25.             next.invokeFlush();  
  26.         }  
  27.     }  
  28.   
  29.     private DefaultChannelHandlerContext findContextOutbound() {  
  30.         DefaultChannelHandlerContext ctx = this;  
  31.         do {  
  32.             ctx = ctx.prev;  
  33.         } while (!ctx.outbound);  
  34.         return ctx;  
  35.     }  
  36.   
  37.     private void invokeWrite(Object msg, ChannelPromise promise) {  
  38.         try {  
  39.             ((ChannelOutboundHandler) handler).write(this, msg, promise);  
  40.         } catch (Throwable t) {  
  41.             notifyOutboundHandlerException(t, promise);  
  42.         }  
  43.     }  
  44.   
  45.     // ...  
  46. }  


context的write()方法沿着context链往前找,直至找到一个outbound类型的context为止,然后调用其invokeWrite()方法:

技术分享

invokeWrite()接着调用handler的write()方法:

技术分享

最后看看ChannelOutboundHandlerAdapter的write()方法实现:

[java] view plain copy 技术分享技术分享
  1. public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {  
  2.     // ...  
  3.     @Override  
  4.     public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {  
  5.         ctx.write(msg, promise);  
  6.     }  
  7.     // ...  
  8. }  


默认的实现调用了context的write()方法而不做任何处理,这样write事件就沿着outbound链继续传播:

技术分享

可见,Pipeline的事件传播,是靠Pipeline,Context和Handler共同协作完成的。


netty入门01