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

位置

回顾一下原来追到的位置

doBind

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

复制代码
1
2
3
4
5
private ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); ... }

initAndReguster

复制代码
1
2
3
4
5
6
7
final ChannelFuture initAndRegister() { ... channel = channelFactory.newChannel(); init(channel); ... }

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

init(channel)

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

复制代码
1
2
3
4
5
6
7
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

复制代码
1
2
3
4
5
6
7
8
9
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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

复制代码
1
2
3
4
5
6
7
8
9
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

复制代码
1
2
public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");

valueOf

复制代码
1
2
3
4
public static <T> ChannelOption<T> valueOf(String name) { return (ChannelOption<T>) pool.valueOf(name); }

pool.valueOf

复制代码
1
2
3
4
5
public T valueOf(String name) { checkNotNullAndNotEmpty(name); return getOrCreate(name); }

getOrCreate

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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

复制代码
1
2
private final ConcurrentMap<String, T> constants = PlatformDependent.newConcurrentHashMap();

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

newConstant

复制代码
1
2
3
4
5
6
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

复制代码
1
2
3
4
private ChannelOption(int id, String name) { super(id, name); }

super

复制代码
1
2
3
4
5
6
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作为守门员。

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

唯一性保证

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
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

复制代码
1
2
3
4
5
6
if (!map.containsKey(key)) return map.put(key, value); else return map.get(key); }

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

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

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

回到外层

复制代码
1
2
3
4
5
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

做什么

复制代码
1
2
3
4
5
6
7
8
synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } }
复制代码
1
2
channel.attr(key).set(e.getValue());

还是挨个赋值

是什么

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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

来源

初始化

复制代码
1
2
3
4
5
6
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的容器上限还是有的,但是它的确可以扩容。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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; }

回顾一下设置的时候

复制代码
1
2
3
final Map<ChannelOption<?>, Object> options = options0(); final Map<AttributeKey<?>, Object> attrs = attrs0();
复制代码
1
2
3
4
5
6
7
final Map<ChannelOption<?>, Object> options0() { return options; } final Map<AttributeKey<?>, Object> attrs0() { return attrs; }

类文件申明

复制代码
1
2
3
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>(); private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();

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

流程中

复制代码
1
2
3
4
5
6
7
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来源小结内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部