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单独创建一个实例,因为存在非共享的状态如成员变量。