首页 > 代码库 > Netty3 源码分析 - ChannelHandler
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单独创建一个实例,因为存在非共享的状态如成员变量。
Netty3 源码分析 - ChannelHandler
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。