不灭的焱

革命尚未成功,同志仍须努力下载JDK17

作者:Albert.Wen  添加时间:2018-09-23 16:34:50  修改时间:2024-05-17 00:35:21  分类:Java框架/系统  编辑

AttributeMap这是是绑定在Channel或者ChannelHandlerContext上的一个附件,相当于依附在这两个对象上的寄生虫一样,相当于附件一样,如图所示:

这个图还算比较形象地描述了AttributeMap的作用,我们知道每一个ChannelHandlerContext都是ChannelHandler和ChannelPipeline之间连接的桥梁,每一个ChannelHandlerContext都有属于自己的上下文,也就说每一个ChannelHandlerContext上如果有AttributeMap都是绑定上下文的,也就说如果A的ChannelHandlerContext中的AttributeMap,B的ChannelHandlerContext是无法读取到的

但是Channel上的AttributeMap就是大家共享的,每一个ChannelHandler都能获取到

我们再看看AttributeMap的结构:

可以看出这个是线程安全的,所以我们可以放心使用,再看看AttributeMap的结构,其实和Map的格式很像,key是AttributeKey,value是Attribute,我们可以根据AttributeKey找到对应的Attribute,并且我们可以指定Attribute的类型T:

我们可以这样使用:

1)首先定义一个AttributeKey:

public static final AttributeKey<NettyChannel> NETTY_CHANNEL_KEY = AttributeKey.valueOf("netty.channel");

我们AttributeMap中存储的是NettyChannel,这是我们自定义的一个类:

package 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中这么使用:

@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获取一个Attribute,如果我们之前没有赋值,那么此时的Attribute值应该是null,我们就创建一个NettyChannel,并使用setIfAbsent这个方法,这个方法是线程安全的,大体的使用方法就是如此

现在我们举两个简单的例子

1)测试ChannelHandler上的AttributeMap是不是上下文绑定的------------首先我们在客户端写两个自定义的ChannelHandler方法,这两个方法的的ChannelActive都会在ChannelHandlerContext上的AttributeMap上写一些属性,然后在对应的ChannelRead方法上读取对应的值,看其是否能读取到:

我们先贴服务端的代码。很简单:

package 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);
             // 绑定端口,开始接收进来的连接
             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:

package 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();
    }
 
}

客户端的bootstrap代码:

package 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();
        }
    
    }
 
}

一个常量类:

package com.lyncc.netty.attributeMap;
 
import io.netty.util.AttributeKey;
 
public class AttributeMapConstant {
    public static final AttributeKey<NettyChannel> NETTY_CHANNEL_KEY = AttributeKey.valueOf("netty.channel");
}

 

两个客户端的handler:

package 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:

package 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();
    }
 
}

我们先运行服务器端:

客户端控制台:

这说明每个ChannelHandlerContext上的AttributeMap是相互不影响的

 

2)测试Channel上的AttributeMap:

我们只需要把2个channel获取Attribute的方法改下就可以了:

Attribute<NettyChannel> attr = ctx.channel().attr(NETTY_CHANNEL_KEY);

两个channel共有四处需要修改,改成获取channel后获取attribute:

再次运行:

好了,首先在ChannelHandler1中赋值了,然后在channelHandlerHandler2中的channelActivew打印了attributeMap中有值了,然后都能够在channelRead中读取到最新值

关于AttributeMap的一些内幕详解,参考一下:

http://blog.csdn.net/zxhoo/article/details/17719333