我是靠谱客的博主 明理花生,最近开发中收集的这篇文章主要介绍netty-option和attr设置位置optionattr来源小结,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

位置

回顾一下原来追到的位置

doBind

bind触发操作之后,追到了doBind

   private ChannelFuture doBind(final SocketAddress localAddress) {
       final ChannelFuture regFuture = initAndRegister();
       ...
   }

initAndReguster

    final ChannelFuture initAndRegister() {
        ...
        channel = channelFactory.newChannel();
        init(channel);
        ...
    }

这一块应该熟悉了,创建channel,接下来深入到init

init(channel)

    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }
        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
       ...
    }

后续child的操作,也是optionattr的设置,这个就不重复。

涉及到的pipeline呢,理解还不通透,先专门把这个简单的解决掉。

option

做什么

setChannelOptions

    static void setChannelOptions(
            Channel channel, Map<ChannelOption<?>, Object> options, InternalLogger logger) {
        for (Map.Entry<ChannelOption<?>, Object> e: options.entrySet()) {
            setChannelOption(channel, e.getKey(), e.getValue(), logger);
        }
    }

setChannelOption

   private static void setChannelOption(
            Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
        try {
            if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
            }
        } catch (Throwable t) {
        }
    }

setOption

    public <T> boolean setOption(ChannelOption<T> option, T value) {
        validate(option, value);
        if (option == CONNECT_TIMEOUT_MILLIS) {
            setConnectTimeoutMillis((Integer) value);
        } else if (option == MAX_MESSAGES_PER_READ) {
            setMaxMessagesPerRead((Integer) value);
        } else if (option == WRITE_SPIN_COUNT) {
            setWriteSpinCount((Integer) value);
        } else if (option == ALLOCATOR) {
            setAllocator((ByteBufAllocator) value);
        } else if (option == RCVBUF_ALLOCATOR) {
            setRecvByteBufAllocator((RecvByteBufAllocator) value);
        } else if (option == AUTO_READ) {
            setAutoRead((Boolean) value);
        } else if (option == AUTO_CLOSE) {
            setAutoClose((Boolean) value);
        } else if (option == WRITE_BUFFER_HIGH_WATER_MARK) {
            setWriteBufferHighWaterMark((Integer) value);
        } else if (option == WRITE_BUFFER_LOW_WATER_MARK) {
            setWriteBufferLowWaterMark((Integer) value);
        } else if (option == WRITE_BUFFER_WATER_MARK) {
            setWriteBufferWaterMark((WriteBufferWaterMark) value);
        } else if (option == MESSAGE_SIZE_ESTIMATOR) {
            setMessageSizeEstimator((MessageSizeEstimator) value);
        } else if (option == SINGLE_EVENTEXECUTOR_PER_GROUP) {
            setPinEventExecutorPerGroup((Boolean) value);
        } else {
            return false;
        }
        return true;
    }

判断option然后进行操作,默认的都是DefaultChannelConfig

可以看到,想要true就只能在有限的选项里面选择,这个是DefaultChannelConfig里面的。

不同的config有自己的设置吧。

追第一个看看

setConnectTimeoutMillis

    public ChannelConfig setConnectTimeoutMillis(int connectTimeoutMillis) {
        if (connectTimeoutMillis < 0) {
            throw new IllegalArgumentException(String.format(
                    "connectTimeoutMillis: %d (expected: >= 0)", connectTimeoutMillis));
        }
        this.connectTimeoutMillis = connectTimeoutMillis;
        return this;
    }

this.connectTimeoutMillis = connectTimeoutMillis;,到头来就是一个赋值操作。

只是不同选项会自动强转,然后赋值。

option就是给channel特定属性赋值的。

是什么

根据名字赋值,不就是个key-value么,甚至都用不上啊。

ChannelOption<T> option到底是什么。

上面不是有几个实例么,看看怎么创建的。

CONNECT_TIMEOUT_MILLIS

public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");

valueOf

    public static <T> ChannelOption<T> valueOf(String name) {
        return (ChannelOption<T>) pool.valueOf(name);
    }

pool.valueOf

    public T valueOf(String name) {
        checkNotNullAndNotEmpty(name);
        return getOrCreate(name);
    }

getOrCreate

    private T getOrCreate(String name) {
        T constant = constants.get(name);
        if (constant == null) {
            final T tempConstant = newConstant(nextId(), name);
            constant = constants.putIfAbsent(name, tempConstant);
            if (constant == null) {
                return tempConstant;
            }
        }

        return constant;
    }

constants

private final ConcurrentMap<String, T> constants = PlatformDependent.newConcurrentHashMap();

原来是String映射的一个对象。

newConstant

    private static final ConstantPool<ChannelOption<Object>> pool = new ConstantPool<ChannelOption<Object>>() {
        protected ChannelOption<Object> newConstant(int id, String name) {
            return new ChannelOption(id, name);
        }
    };

ChannelOption

    private ChannelOption(int id, String name) {
        super(id, name);
    }

super

    protected AbstractConstant(int id, String name) {
        this.id = id;
        this.name = name;
        this.uniquifier = uniqueIdGenerator.getAndIncrement();
    }

大爷的,就是创建了一个对象,而且是唯一变量而已。

看一下族谱
在这里插入图片描述
还看到了AttributeKey,后面再说。

实现Constant,也就是说,是个常量。

总结下来,我们以为的option虽然是key-value形式,但它是通过key的唯一性来准确设置value

keyChannelOption形式,又是通过key-value的形式,用String唯一确定一个ChannelOption

并保存在currentHashMap当中,画个图梳理一下

Created with Raphaël 2.2.0 String search constant contains? get uniq option search OptionMap has this Option? set optionValue true end false yes no yes no

一般的设置属性其实都是key-value属性,为了准确设置,必须保证key的唯一性。

这个过程当中,抛弃String,用ChannelOption作为守门员。

同时,限定了可设置的属性,非关键属性都不可被设置,对属性,和参数都有严格校验。

唯一性保证

    private T getOrCreate(String name) {
        T constant = constants.get(name);
        if (constant == null) {
            final T tempConstant = newConstant(nextId(), name);
            constant = constants.putIfAbsent(name, tempConstant);
            if (constant == null) {
                return tempConstant;
            }
        }
        return constant;
    }

关于唯一性实现总有这么一个阶段

  1. 直接设置

  2. map查询

  3. currentHashMap查询(多线程环境)

  4. 竞态条件

putIfAbsent

    if (!map.containsKey(key))
        return map.put(key, value);
     else
        return map.get(key);
     }

你说,这代码有什么问题呢?没问题啊。

深入竞态条件,如果两个线程都进入了if,其实后续的操作都是直接put

也就是说,竞态条件下,其实返回的是两个值,而不是同一个对象。

回到外层

     constant = constants.putIfAbsent(name, tempConstant);
     if (constant == null) {
          return tempConstant;
     }

也就是说,竞态条件下,为null的就直接返回刚创建的对象。

因为即使是先进入、先创建对象的线优先进入了竞太条件内部,却不能够保证优先写入。

我们需要唯一key指向的唯一的对象,如果直接返回,就不能够保证同key值的单一value对象。

正所谓成王败寇,谁先写入了,谁就是皮卡丘。

更多信息可以看这个,还有这个。

有哪些

optiontype
CONNECT_TIMEOUT_MILLISInteger
MAX_MESSAGES_PER_READInteger
WRITE_SPIN_COUNTInteger
ALLOCATORByteBufAllocator
RCVBUF_ALLOCATORRecvByteBufAllocator
AUTO_READBoolean
AUTO_CLOSEBoolean
WRITE_BUFFER_HIGH_WATER_MARKInteger
WRITE_BUFFER_LOW_WATER_MARKInteger
WRITE_BUFFER_WATER_MARKWriteBufferWaterMark
MESSAGE_SIZE_ESTIMATORMessageSizeEstimator
SINGLE_EVENTEXECUTOR_PER_GROUPBoolean

想加一个description的,发现懂得太少,不丢人现眼了。

attr

做什么

        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
channel.attr(key).set(e.getValue());

还是挨个赋值

是什么

public interface AttributeMap {
    /**
     * Get the {@link Attribute} for the given {@link AttributeKey}. This method will never return null, but may return
     * an {@link Attribute} which does not have a value set yet.
     */
    <T> Attribute<T> attr(AttributeKey<T> key);

    /**
     * Returns {@code} true if and only if the given {@link Attribute} exists in this {@link AttributeMap}.
     */
    <T> boolean hasAttr(AttributeKey<T> key);
}

哎,不追了,数据结构什么的后续慢慢充实吧。

可以看到,也是一个key-value的键值,底层想必也是做了安全措施的。

有哪些

option对比一下吧,一个固定的,一个不固定。

option就好比object,一个对象的属性必然是可列举的。

attr就好比map,只是一个容器,可有可无,灵活多变。

根本来看,option是既有程序的控制参数,控制已有的,不受我们操作的底层流程。

因而可以枚举,肯定可以设置,只是参数个数的问题。

但是attr是我们自己程序中用来在流程中传递元素的手段,底层代码必定用不上,个数、类型也未可知。

它就是我们附带信息并传递信息的一个手段,类似于session

来源

初始化

            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ServerInitializer())
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 200)
                    .attr(AttributeKey.newInstance("name"), "godme");

就是如此,我们可以初始化时通过

option:设置option

attr:设置attr

相同的是都可以不设置。

不同的是,option有默认值,但是attr啥也没有。

同时,option有效设置个数是有上限的,但是attr可以没有上限。

不过你仔细追一下,attr的容器上限还是有的,但是它的确可以扩容。

    public <T> B option(ChannelOption<T> option, T value) {
        if (option == null) {
            throw new NullPointerException("option");
        }
        if (value == null) {
            synchronized (options) {
                options.remove(option);
            }
        } else {
            synchronized (options) {
                options.put(option, value);
            }
        }
        return (B) this;
    }

    public <T> B attr(AttributeKey<T> key, T value) {
        if (key == null) {
            throw new NullPointerException("key");
        }
        if (value == null) {
            synchronized (attrs) {
                attrs.remove(key);
            }
        } else {
            synchronized (attrs) {
                attrs.put(key, value);
            }
        }
        return (B) this;
    }

回顾一下设置的时候

        final Map<ChannelOption<?>, Object> options = options0();
        final Map<AttributeKey<?>, Object> attrs = attrs0();
    final Map<ChannelOption<?>, Object> options0() {
        return options;
    }
    final Map<AttributeKey<?>, Object> attrs0() {
        return attrs;
    }

类文件申明

    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();

前面设置只是记录,bind触发后真正设置,你问什么时候使用?我母鸡啊!

流程中

    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println("time from server:" + msg);
        ctx.channel().attr(AttributeKey.newInstance("name")).set("godme");
        ctx.channel().hasAttr(AttributeKey.newInstance("name"));
        ctx.channel().attr(AttributeKey.newInstance("name")).get();
    }

经过的handler,通过这样,就可以相互之间传递信息了。

小结

前面说缓冲区设置策略的时候,也是对config进行的设置。对应的话,应该是RCVBUF_ALLOCATOR吧。

这一次也不知道全不全,但肯定不够深,慢慢学着走呗。

最后

以上就是明理花生为你收集整理的netty-option和attr设置位置optionattr来源小结的全部内容,希望文章能够帮你解决netty-option和attr设置位置optionattr来源小结所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部