本來(lái)沒(méi)打算研究這個(gè)東西的,一開(kāi)始覺(jué)得沒(méi)啥用,甚至覺(jué)得這個(gè)東西有點(diǎn)雞肋,不過(guò)慢慢接觸之后,發(fā)現了這個(gè)AttributeMap的重要性
初學(xué)這個(gè)東西,我們還是先理解AttributeMap的用法吧
1)AttributeMap這是是綁定在Channel或者ChannelHandlerContext上的一個(gè)附件,相當于依附在這兩個(gè)對象上的寄生蟲(chóng)一樣,相當于附件一樣,如圖所示:
這個(gè)圖還算比較形象地描述了AttributeMap的作用,我們知道每一個(gè)ChannelHandlerContext都是ChannelHandler和ChannelPipeline之間連接的橋梁,每一個(gè)ChannelHandlerContext都有屬于自己的上下文,也就說(shuō)每一個(gè)ChannelHandlerContext上如果有AttributeMap都是綁定上下文的,也就說(shuō)如果A的ChannelHandlerContext中的AttributeMap,B的ChannelHandlerContext是無(wú)法讀取到的
但是Channel上的AttributeMap就是大家共享的,每一個(gè)ChannelHandler都能獲取到
我們再看看AttributeMap的結構:
可以看出這個(gè)是線(xiàn)程安全的,所以我們可以放心使用,再看看AttributeMap的結構,其實(shí)和Map的格式很像,key是AttributeKey,value是Attribute,我們可以根據AttributeKey找到對應的Attribute,并且我們可以指定Attribute的類(lèi)型T:
我們可以這樣使用:
1)首先定義一個(gè)AttributeKey:
[java]
view plain copypublic static final AttributeKey<NettyChannel> NETTY_CHANNEL_KEY = AttributeKey.valueOf("netty.channel");
我們AttributeMap中存儲的是NettyChannel,這是我們自定義的一個(gè)類(lèi):
[java]
view plain copypackage com.lyncc.netty.attributeMap;
import java.util.Date;
public class NettyChannel {
private String name;
private Date createDate;
public NettyChannel(String name,Date createDate) {
this.name = name;
this.createDate = createDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
}
那么我們可以這么使用ChannelHandler中這么使用:[java]
view plain copy@Override
public void channelActive(ChannelHandlerContext ctx) {
Attribute<NettyChannel> attr = ctx.attr(NETTY_CHANNEL_KEY);
NettyChannel nChannel = attr.get();
if (nChannel == null) {
NettyChannel newNChannel = new NettyChannel("HelloWorld0Client", new Date());
nChannel = attr.setIfAbsent(newNChannel);
} else {
System.out.println("attributeMap 中是有值的");
System.out.println(nChannel.getName() + "=======" + nChannel.getCreateDate());
}
System.out.println("HelloWorldC0ientHandler Active");
ctx.fireChannelActive();
}
channelActive方法中的ChannelHandlerContext方法可以使用attr方法傳入AttributeKey獲取一個(gè)Attribute,如果我們之前沒(méi)有賦值,那么此時(shí)的Attribute值應該是null,我們就創(chuàng )建一個(gè)NettyChannel,并使用setIfAbsent這個(gè)方法,這個(gè)方法是線(xiàn)程安全的,大體的使用方法就是如此
現在我們舉兩個(gè)簡(jiǎn)單的例子
1)測試ChannelHandler上的AttributeMap是不是上下文綁定的------------首先我們在客戶(hù)端寫(xiě)兩個(gè)自定義的ChannelHandler方法,這兩個(gè)方法的的ChannelActive都會(huì )在ChannelHandlerContext上的AttributeMap上寫(xiě)一些屬性,然后在對應的ChannelRead方法上讀取對應的值,看其是否能讀取到:
我們先貼服務(wù)端的代碼。很簡(jiǎn)單:
[java]
view plain copypackage com.lyncc.netty.attributeMap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
public class HelloWorldServer {
private int port;
public HelloWorldServer(int port) {
this.port = port;
}
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("decoder", new StringDecoder());
ch.pipeline().addLast("encoder", new StringEncoder());
ch.pipeline().addLast(new HelloWorldServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 綁定端口,開(kāi)始接收進(jìn)來(lái)的連接
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port );
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new HelloWorldServer(port).start();
}
}
Channel:[java]
view plain copypackage com.lyncc.netty.attributeMap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class HelloWorldServerHandler extends ChannelInboundHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("server channelRead..");
System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());
ctx.write("server write"+msg);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客戶(hù)端的bootstrap代碼:[java]
view plain copypackage com.lyncc.netty.attributeMap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
public class HelloWorldClient {
static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
static final int SIZE = Integer.parseInt(System.getProperty("size", "256"));
public static void main(String[] args) throws Exception {
initChannel();
}
public static void initChannel() throws InterruptedException{
// Configure the client.
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("decoder", new StringDecoder());
p.addLast("encoder", new StringEncoder());
p.addLast(new HelloWorldClientHandler());
p.addLast(new HelloWorld2ClientHandler());
}
});
ChannelFuture future = b.connect(HOST, PORT).sync();
future.channel().writeAndFlush("hello Netty,Test attributeMap");
future.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
一個(gè)常量類(lèi):
[java]
view plain copypackage com.lyncc.netty.attributeMap;
import io.netty.util.AttributeKey;
public class AttributeMapConstant {
public static final AttributeKey<NettyChannel> NETTY_CHANNEL_KEY = AttributeKey.valueOf("netty.channel");
}
兩個(gè)客戶(hù)端的handler:[java]
view plain copypackage com.lyncc.netty.attributeMap;
import static com.lyncc.netty.attributeMap.AttributeMapConstant.NETTY_CHANNEL_KEY;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.Attribute;
import java.util.Date;
public class HelloWorldClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
Attribute<NettyChannel> attr = ctx.attr(NETTY_CHANNEL_KEY);
NettyChannel nChannel = attr.get();
if (nChannel == null) {
NettyChannel newNChannel = new NettyChannel("HelloWorld0Client", new Date());
nChannel = attr.setIfAbsent(newNChannel);
} else {
System.out.println("channelActive attributeMap 中是有值的");
System.out.println(nChannel.getName() + "=======" + nChannel.getCreateDate());
}
System.out.println("HelloWorldC0ientHandler Active");
ctx.fireChannelActive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Attribute<NettyChannel> attr = ctx.attr(NETTY_CHANNEL_KEY);
NettyChannel nChannel = attr.get();
if (nChannel == null) {
NettyChannel newNChannel = new NettyChannel("HelloWorld0Client", new Date());
nChannel = attr.setIfAbsent(newNChannel);
} else {
System.out.println("channelRead attributeMap 中是有值的");
System.out.println(nChannel.getName() + "=======" + nChannel.getCreateDate());
}
System.out.println("HelloWorldClientHandler read Message:" + msg);
ctx.fireChannelRead(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
handler2:[java]
view plain copypackage com.lyncc.netty.attributeMap;
import static com.lyncc.netty.attributeMap.AttributeMapConstant.NETTY_CHANNEL_KEY;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.Attribute;
import java.util.Date;
public class HelloWorld2ClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) {
Attribute<NettyChannel> attr = ctx.attr(NETTY_CHANNEL_KEY);
NettyChannel nChannel = attr.get();
if (nChannel == null) {
NettyChannel newNChannel = new NettyChannel("HelloWorld2Client", new Date());
nChannel = attr.setIfAbsent(newNChannel);
} else {
System.out.println("channelActive attributeMap 中是有值的");
System.out.println(nChannel.getName() + "=======" + nChannel.getCreateDate());
}
System.out.println("HelloWorldC2ientHandler Active");
ctx.fireChannelActive();
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
Attribute<NettyChannel> attr = ctx.attr(NETTY_CHANNEL_KEY);
NettyChannel nChannel = attr.get();
if (nChannel == null) {
NettyChannel newNChannel = new NettyChannel("HelloWorld0Client", new Date());
nChannel = attr.setIfAbsent(newNChannel);
} else {
System.out.println("channelRead attributeMap 中是有值的");
System.out.println(nChannel.getName() + "=======" + nChannel.getCreateDate());
}
System.out.println("HelloWorldClientHandler read Message:" + msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
我們先運行服務(wù)器端:
客戶(hù)端控制臺:
這說(shuō)明每個(gè)ChannelHandlerContext上的AttributeMap是相互不影響的
2)測試Channel上的AttributeMap:
我們只需要把2個(gè)channel獲取Attribute的方法改下就可以了:
[java]
view plain copyAttribute<NettyChannel> attr = ctx.channel().attr(NETTY_CHANNEL_KEY);
兩個(gè)channel共有四處需要修改,改成獲取channel后獲取attribute:
再次運行:
好了,首先在ChannelHandler1中賦值了,然后在channelHandlerHandler2中的channelActivew打印了attributeMap中有值了,然后都能夠在channelRead中讀取到最新值
原作者鏈接http://blog.csdn.net/zxhoo/article/details/17719333