Netty3 源码分析 - ChannelHandler

Netty3 源码分析 - ChannelHandler

每个通道关联一个Pipeline,在流水线中拦截处理各种事件的对象就是ChannelHandler,它处理ChannelEvent而后进行传递。
接口 ChannelHandler没有提供任何方法,有两个子接口分别用来规范处理上行和下行的通道事件。


ChannelHandler是随ChannelHandlerContext对象提供的,handler通过这个context对象参与这个Pipeline的交互管理,通过它所属的context对象,一个handler可以传递上行或下行事件,动态改变流水线,存储信息到attachment。

状态管理:一个 ChannelHandler通常需要存一些状态信息,最简单推荐的方式就是利用成员变量(放在特定的Handler类中), 向下面这样:
public   class  DataServerHandler  extends  SimpleChannelHandler {

       private   boolean   loggedIn  // 存储状态信息

       @ Override
     public   void  messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object o = e.getMessage();
         if  (o  instanceof  LoginMessage) {
            authenticate((LoginMessage ) o);
            loggedIn =  true  ;
        }  else  (o  instanceof  GetDataMessage) {
             if  (loggedIn) {
                ch.write( fetchSecret((GetDataMessage ) o));
            }  else  {
                fail();
            }
        }
    }
}
因为每个Handler实例是服务于一个连接的,所以对于每个新的Channel都应该创建一个新的 ChannelHandler实例。
//Create a new handler instance per channel.
// See Bootstrap.setPipelineFactory(ChannelPipelineFactory).
public   class  DataServerPipelineFactory  implements  ChannelPipelineFactory {
       public  ChannelPipeline getPipeline() {
            return  Channels.pipeline(  new  DataServerHandler()); //注意这里
     }
}

但是有时候并不需要很多冗余Handler,做着相同的工作,不需要为每个连接(或Channel)都创建一个这样的Handler,所以可以利用attachment存储状态信息,(或许可以认为这样的Handler是安全的,可重入的)。 向下面这样:
@Sharable
public   class  DataServerHandler2  extends  SimpleChannelHandler {

       @ Override
     public   void  messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object o = e.getMessage();
         if  (o  instanceof  LoginMessage) {
            authenticate((LoginMessage ) o);
            ctx.setAttachment(  true  );
        }  else  (o  instanceof  GetDataMessage) {
             if  (Boolean.TRUE.equals(ctx.getAttachment())) {
                ch.write( fetchSecret((GetDataMessage ) o));
            }  else  {
                fail();
            }
        }
    }
}

这样就可以为不同的Pipeline增加同一个Handler,如下:
public   class  DataServerPipelineFactory2  implements  ChannelPipelineFactory {

       private   static   final  ChannelHandler  SHARED  =  new  DataServerHandler2();

       public  ChannelPipeline getPipeline() {
            return  Channels.pipeline(  new  ChannelHandler[] {  SHARED  });
     }
}

使用ChannelLocal:如果有的状态变量需要从其他Handler或者 Handler 之外来访问,就需要用
ChannelLocal这个Iterable,相当于从属于这个Channel的全局变量,可以联想TreadLocal
public   final  class  DataServerState {

       public   static   final  ChannelLocal<Boolean>  loggedIn  =  new  ChannelLocal<Boolean>() {
            protected  Boolean initialValue(Channel channel) {
                return   false  ;
          }
     };
}
Handler此时这样写:
@  Sharable
public   class  DataServerHandler  extends  SimpleChannelHandler {

       @ Override
     public   void  messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
        Channel ch = e.getChannel();
        Object o = e.getMessage();
         if  (o  instanceof  LoginMessage) {
            authenticate((LoginMessage ) o);
            DataServerState.loggedIn.set(ch,  true  ); // 更新这个ChannelLocal
        }  else  (o  instanceof  GetDataMessage) {
             if  (DataServerState.loggedIn.get(ch)) {
                ctx.getChannel().write(fetchSecret(( GetDataMessage) o));
            }  else  {
                fail();
            }
        }
    }
}
根据每个Channel的状态进行逻辑处理,使用场景:
          // Print the remote addresses of the authenticated clients:
           ChannelGroup allClientChannels = ...;
            for  (Channel ch: allClientChannels) {
                if  (DataServerState.loggedIn.get(ch)) {
                   System.out.println(ch.getRemoteAddress());
               }
           }

@Sharable注解:如果一个ChannelHandler前面有该注解,意味着可以将该对象的一个实例分配给多个Pipeline,而不会发生竟态条件。否则的话就要为每个Pipeline单独创建一个实例,因为存在非共享的状态如成员变量。










你可能感兴趣的:(channelhandler,ChannelLocal,ChannelContext,Shareable)