Netty4学习笔记(7)-- AttributeMap

9 篇文章 9 订阅
9 篇文章 0 订阅

IoSession

MINAIoSession接口定义了一组方法,让我们可以利用IoSession来存储一些数据:

public interface IoSession {
    getAttribute(Object key)
    getAttribute(Object key, Object defaultValue)
    setAttribute(Object key)
    setAttribute(Object key, Object value)
    setAttributeIfAbsent(Object key)
    setAttributeIfAbsent(Object key, Object value)
    replaceAttribute(Object key, Object oldValue, Object newValue)
    removeAttribute(Object key)
    removeAttribute(Object key, Object value)
    containsAttribute(Object key)
    getAttributeKeys() 
}

AttributeMap接口

Netty将这种看似Map的功能进一步抽象,形成了 AttributeMap接口:

public interface AttributeMap {
    <T> Attribute<T> attr(AttributeKey<T> key);
}

AttributeMap接口只有一个attr()方法,接收一个AttributeKey类型的key,返回一个Attribute类型的value。按照Javadoc,AttributeMap实现必须是线程安全的。AttributeMap内部结构看起来像下面这样:


谁实现了AttributeMap接口?

答案是所有的ChannelChannelHandlerContext,如下面的类图所示:


AttributeKey

AttributeKey有两个地方值得 一提。第一是AttributeKey是个泛型类,在我看来,这也是Netty相对于MINA的一处改进。在使用IoSession的时候,你必须进行强制类型转换:

int userId = (Integer) ioSession.getAttribute("userId");
但是使用AttributeMap却不需要:

AttributeKey<Integer> KEY_USER_ID = AttributeKey.valueOf("userId");
int userId = channel.attr(KEY_USER_ID).get();

第二是AttributeKey继承了 UniqueName类,也就是说,对于每一个name,应该只有唯一一个AttributeKey与之对应。这一点看起来很奇怪,但是当知道 DefaultAttributeMap内部使用了 IdentityHashMap的时候,就觉得合情合理。下面是与AttributeKey相关的类图,至于UniqueName如何保证name唯一,稍后介绍:



UniqueName

UniqueName实际上是靠传入构造函数的一个map来保证name的唯一性:

@Deprecated
public class UniqueName implements Comparable<UniqueName> {

    private static final AtomicInteger nextId = new AtomicInteger();

    private final int id;
    private final String name;

    public UniqueName(ConcurrentMap<String, Boolean> map, String name, Object... args) {
        if (map == null) {
            throw new NullPointerException("map");
        }
        if (name == null) {
            throw new NullPointerException("name");
        }
        if (args != null && args.length > 0) {
            validateArgs(args);
        }

        if (map.putIfAbsent(name, Boolean.TRUE) != null) {
            throw new IllegalArgumentException(String.format("'%s' is already in use", name));
        }

        id = nextId.incrementAndGet();
        this.name = name;
    }
    ...
}
但是Javadoc说这个类存在跟类加载器相关的问题,所以被废弃了。AttributeKey继承了UniqueName,内部使用ConcurrentHashMap来保证name的唯一性:

public final class AttributeKey<T> extends UniqueName {

    private static final ConcurrentMap<String, Boolean> names = PlatformDependent.newConcurrentHashMap();

    @SuppressWarnings("deprecation")
    public static <T> AttributeKey<T> valueOf(String name) {
        return new AttributeKey<T>(name);
    }

    @Deprecated
    public AttributeKey(String name) {
        super(names, name);
    }
}

Attribute接口

Attribute接口除了有必须的get()set()remove()方法外,还有几个原子方法:

public interface Attribute<T> {
    AttributeKey<T> key();
    T get();
    void set(T value);
    T getAndSet(T value);
    T setIfAbsent(T value);
    T getAndRemove();
    boolean compareAndSet(T oldValue, T newValue);
    void remove();
}

DefaultAttributeMap

如前面的类图所示,DefaultAttributeMap实现了AttributeMap接口,AbstractChannelDefaultChannelHandlerContext通过继承DefaultAttributeMap也实现了AttributeMap接口。下面是DefaultAttributeMap的部分代码:

public class DefaultAttributeMap implements AttributeMap {

    @SuppressWarnings("rawtypes")
    private static final AtomicReferenceFieldUpdater<DefaultAttributeMap, Map> updater =
            AtomicReferenceFieldUpdater.newUpdater(DefaultAttributeMap.class, Map.class, "map");

    // Initialize lazily to reduce memory consumption; updated by AtomicReferenceFieldUpdater above.
    @SuppressWarnings("UnusedDeclaration")
    private volatile Map<AttributeKey<?>, Attribute<?>> map;

    @Override
    public <T> Attribute<T> attr(AttributeKey<T> key) {
        Map<AttributeKey<?>, Attribute<?>> map = this.map;
        if (map == null) {
            // Not using ConcurrentHashMap due to high memory consumption.
            map = new IdentityHashMap<AttributeKey<?>, Attribute<?>>(2);
            if (!updater.compareAndSet(this, null, map)) {
                map = this.map;
            }
        }

        synchronized (map) {
            @SuppressWarnings("unchecked")
            Attribute<T> attr = (Attribute<T>) map.get(key);
            if (attr == null) {
                attr = new DefaultAttribute<T>(map, key);
                map.put(key, attr);
            }
            return attr;
        }
    }
    ...
}
可以看到:

  1. map是延迟创建的(为了减少内存消耗),更准确的说,map在attr()方法第一次被调用的时候创建
  2. map被声明为volatile,再加上AtomicReferenceFieldUpdater.compareAndSet()方法的使用,map的null判断和赋值这段代码可以不使用synchronized
  3. 内部使用的是IdentityHashMap,所以AttributeKey必须是唯一的,因为IdentityHashMap使用==而不是equals()方法来判断两个key是否相同
  4. attr()方法被调用时,如果key还没有关联attribute,会自动创建一个


DefaultAttribute

最后,DefaultAttribute通过继承AtomicReference获得了原子操作能力:

public class DefaultAttributeMap implements AttributeMap {
    private static final class DefaultAttribute<T> extends AtomicReference<T> implements Attribute<T> {

        private static final long serialVersionUID = -2661411462200283011L;

        private final Map<AttributeKey<?>, Attribute<?>> map;
        private final AttributeKey<T> key;

        DefaultAttribute(Map<AttributeKey<?>, Attribute<?>> map, AttributeKey<T> key) {
            this.map = map;
            this.key = key;
        }

        @Override
        public AttributeKey<T> key() {
            return key;
        }

        @Override
        public T setIfAbsent(T value) {
            while (!compareAndSet(null, value)) {
                T old = get();
                if (old != null) {
                    return old;
                }
            }
            return null;
        }

        @Override
        public T getAndRemove() {
            T oldValue = getAndSet(null);
            remove0();
            return oldValue;
        }

        @Override
        public void remove() {
            set(null);
            remove0();
        }

        private void remove0() {
            synchronized (map) {
                map.remove(key);
            }
        }
    }
}
下面是DefaultAttributeMap的内部结构:

结论

  • ChannelChannelHandlerContext都扩展了AttributeMap接口,因此每一个Channel和ChannelHandlerContext实例都可以像Map一样按照key来存取value
  • AttributeMap实现必须是线程安全的,因此,attr()方法可以在任何线程里安全的调用
  • AttributeKey必须是唯一的,因此最好定义成全局变量(比如static final类型)
  • 默认的Attribute实现继承自AtomicReference,因此也是线程安全的


  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: Netty-WebSocket-Spring-Boot-Starter是一个用于将Websocket集成到Spring Boot应用程序中的库。它使用Netty作为底层框架,提供了一种快速和可靠的方式来处理异步通信。 这个库提供了一种简单的方法来创建Websocket端点,只需要使用注释和POJO类即可。在这些端点上可以添加动态的事件处理程序,以处理连接、断开连接和消息事件等。 此外,Netty-WebSocket-Spring-Boot-Starter还包括了一些安全性的特性,如基于令牌的授权和XSS保护,可以帮助您保持您的Websocket应用程序安全。 总的来说,Netty-WebSocket-Spring-Boot-Starter提供了一种快速和易于使用的方式来构建Websocket应用程序,使得它成为应用程序开发人员的有用工具。 ### 回答2: netty-websocket-spring-boot-starter 是一个开源的 Java Web 开发工具包,主要基于 Netty 框架实现了 WebSocket 协议的支持,同时集成了 Spring Boot 框架,使得开发者可以更加方便地搭建 WebSocket 服务器。 该工具包提供了 WebSocketServer 配置类,通过在 Spring Boot 的启动配置类中调用 WebSocketServer 配置类,即可启动 WebSocket 服务器。同时,该工具包还提供了多种配置参数,如端口号、URI 路径、SSL 配置、认证配置等等,可以根据业务需求进行自定义配置。 此外,该工具包还提供了一些可扩展的接口和抽象类,如 WebSocketHandler、ChannelHandlerAdapter 等,可以通过继承和实现这些接口和抽象类来实现业务逻辑的处理和拓展。 总的来说,netty-websocket-spring-boot-starter 提供了一个高效、简单、易用的 WebSocket 服务器开发框架,可以减少开发者的开发成本和工作量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值