Channel Handler

继承结构

ChannelHandler的类继承结构如下:

@startuml

ChannelHandler <|-left- ChannelInboundHandler
ChannelHandler <|-right- ChannelOutboundHandler

ChannelHandler <|-- ChannelHandlerAdapter

ChannelInboundHandler <|-- ChannelInboundHandlerAdapter
ChannelHandlerAdapter <|-- ChannelInboundHandlerAdapter

ChannelOutboundHandler <|-- ChannelOutboundHandlerAdapter
ChannelHandlerAdapter <|-- ChannelOutboundHandlerAdapter

ChannelInboundHandlerAdapter <|-- ChannelDuplexHandler
ChannelOutboundHandler <|-- ChannelDuplexHandler

ChannelInboundHandlerAdapter <|-- ChannelInitializer

interface ChannelHandler {
+ handlerAdded(ctx)
+ handlerRemoved(ctx)
+ @interface Sharable
}

interface ChannelInboundHandler {
+ void channelRegistered(ctx)
+ void channelUnregistered(ctx)
+ void channelActive(ctx)
+ void channelInactive(ctx)
+ void channelRead(ctx, Object msg)
+ void channelReadComplete(ctx)
+ void userEventTriggered(ctx, evt)
+ void channelWritabilityChanged(ctx)
+ void exceptionCaught(ctx, cause)
}

interface ChannelOutboundHandler {
+ void bind(ctx, localAddress, promise)
+ void connect(ctx, remoteAddress, localAddress, promise)
+ void disconnect(ctx, promise)
+ void close(ctx, promise)
+ void deregister(ctx, promise)
+ void read(ctx)
+ void write(ctx, msg, promise)
+ void flush(ctx)
}

abstract class ChannelHandlerAdapter {
~ boolean added
}

class ChannelInboundHandlerAdapter {
}

class ChannelOutboundHandlerAdapter {
}

class ChannelDuplexHandler {
}

abstract ChannelInitializer {
# {abstract} void initChannel(ch)
}

@enduml

功能

Channel Handler的功能, 如在javadoc中所说:

处理I/O事件或者拦截I/O操作, 并转发给它所在ChannelPipeline中的下一个handler.

子类型

ChannelHandler本身并不提供很多方法, 但是通常需要实现它的子类型之一:

  • ChannelInboundHandler 用于处理inbound I/O事件
  • ChannelOutboundHandler 用于处理outbound I/O事件

或者, 为了方便使用, 提供了下面的adapter 类:

  • ChannelInboundHandlerAdapter 用于处理inbound I/O事件
  • ChannelOutboundHandlerAdapter 用于处理outbound I/O事件
  • ChannelDuplexHandler 用于同时处理 inbound 和 outbound事件

上下文对象

ChannelHandler伴随有一个ChannelHandlerContext对象. ChannelHandler被设定为通过一个context对象来和它所属的ChannelPipeline交互.

通过使用这个context对象, ChannelHandler可以在上游和下游中传递事件, 动态修改pipeline, 或者存储handler特定的信息(使用AttributeKeys).

状态管理

ChannelHandler通常需要存储一些状态化的信息. 最简单的而且推荐的方式是使用成员变量:

使用成员变量

public interface Message {
 // your methods here
}

public class DataServerHandler extends SimpleChannelInboundHandler<Message> {

    private boolean loggedIn;

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Message message) {
     Channel ch = e.getChannel();
     if (message instanceof LoginMessage) {
         authenticate((LoginMessage) message);
         loggedIn = true;
     } else (message instanceof GetDataMessage) {
         if (loggedIn) {
             ch.write(fetchSecret((GetDataMessage) message));
         } else {
             fail();
         }
     }
    }
    ...
}

因为这个handler实例有一个归属于某一个特定连接的状态变量, 我们不得不为每个新channel创建一个新的handler实例来.

// Create a new handler instance per channel.
// See ChannelInitializer.initChannel(Channel).
public class DataServerInitializer extends ChannelInitializer<Channel> {
 @Override
 public void initChannel(Channel channel) {
     // 注意这里的DataServerHandler的实例是每次initChannel时都全新new出来一个
    // 这保证了DataServerHandler实例和channel的严格一对一关系
    channel.pipeline().addLast("handler", new DataServerHandler());
 }
}

使用AttributeKeys

Although it's recommended to use member variables to store the state of a handler, for some reason you might not want to create many handler instances. In such a case, you can use AttributeKeys which is provided by ChannelHandlerContext:

虽然推荐使用成员变量来存储handler的状态, 但是因为某些原因,可能不想创建太多的handler实例. 在这种情况下, 可以使用ChannelHandlerContext提供的AttributeKeys:

public interface Message {
 // your methods here
}

@Sharable
public class DataServerHandler extends SimpleChannelInboundHandler<Message> {
 // 申明一个AttributeKey
 private final AttributeKey<Boolean> auth = AttributeKey.valueOf("auth");

 @Override
 public void channelRead(ChannelHandlerContext ctx, Message message) {
      // 从context对象中获取AttributeKey对应的Attribute
     Attribute<Boolean> attr = ctx.attr(auth);
     Channel ch = ctx.channel();
     if (message instanceof LoginMessage) {
         authenticate((LoginMessage) o);
         // 保存结果到Attribute
         attr.set(true);
     } else (message instanceof GetDataMessage) {
          // 检查Attribute保存的值
         if (Boolean.TRUE.equals(attr.get())) {
             ch.write(fetchSecret((GetDataMessage) o));
         } else {
             fail();
         }
     }
 }
 ...
}

现在handler的状态被附加在ChannelHandlerContext, 你可以将同一个handler实例添加到不同的pipeline(对应不同的channel):

public class DataServerInitializer extends ChannelInitializer<Channel> {

 // 注意这里的DataServerHandler的实例是共享的, 只有一个
 private static final DataServerHandler SHARED = new DataServerHandler();

 @Override
 public void initChannel(Channel channel) {
      // 所有的channel和pipeline, 都使用这同一个handler的实例
     channel.pipeline().addLast("handler", SHARED);
 }
}

@Sharable 标签

在上面的使用AttributeKey的例子中, 你可能已经发现@Sharable标签.

如果一个ChannelHandler被标注为@Sharable标签, 这意味着你可以仅仅创建这个handler的一个实例然后将它多次添加到一个或者多个ChannelPipelines中, 不会导致冲突或竞争.

如果没有特别标明这个标签, 你将不得不在每次你将它添加到pipeline时创建一个新的handler实例, 因为它有不共享的状态例如成员变量.

这个标签仅用于文档目的.

results matching ""

    No results matching ""