位置
回顾一下原来追到的位置
doBind
bind
触发操作之后,追到了doBind
1
2
3
4
5private ChannelFuture doBind(final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); ... }
initAndReguster
1
2
3
4
5
6
7final 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
16void 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
的操作,也是option
和attr
的设置,这个就不重复。涉及到的
pipeline
呢,理解还不通透,先专门把这个简单的解决掉。
option
做什么
setChannelOptions
1
2
3
4
5
6
7static 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
9private 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
32public <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
9public 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
2public static final ChannelOption<Integer> CONNECT_TIMEOUT_MILLIS = valueOf("CONNECT_TIMEOUT_MILLIS");
valueOf
1
2
3
4public static <T> ChannelOption<T> valueOf(String name) { return (ChannelOption<T>) pool.valueOf(name); }
pool.valueOf
1
2
3
4
5public T valueOf(String name) { checkNotNullAndNotEmpty(name); return getOrCreate(name); }
getOrCreate
1
2
3
4
5
6
7
8
9
10
11
12
13private 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
2private final ConcurrentMap<String, T> constants = PlatformDependent.newConcurrentHashMap();
原来是
String
映射的一个对象。
newConstant
1
2
3
4
5
6private 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
4private ChannelOption(int id, String name) { super(id, name); }
super
1
2
3
4
5
6protected AbstractConstant(int id, String name) { this.id = id; this.name = name; this.uniquifier = uniqueIdGenerator.getAndIncrement(); }
大爷的,就是创建了一个对象,而且是唯一变量而已。
看一下族谱
还看到了AttributeKey
,后面再说。
实现Constant
,也就是说,是个常量。
总结下来,我们以为的option
虽然是key-value
形式,但它是通过key
的唯一性来准确设置value
。
而key
是ChannelOption
形式,又是通过key-value
的形式,用String
唯一确定一个ChannelOption
。
并保存在currentHashMap
当中,画个图梳理一下
一般的设置属性其实都是key-value
属性,为了准确设置,必须保证key
的唯一性。
这个过程当中,抛弃String
,用ChannelOption
作为守门员。
同时,限定了可设置的属性,非关键属性都不可被设置,对属性,和参数都有严格校验。
唯一性保证
复制代码1
2
3
4
5
6
7
8
9
10
11
12private 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; }
关于唯一性实现总有这么一个阶段
直接设置
map
查询
currentHashMap
查询(多线程环境)防
竞态条件
putIfAbsent
复制代码1
2
3
4
5
6if (!map.containsKey(key)) return map.put(key, value); else return map.get(key); }
你说,这代码有什么问题呢?没问题啊。
深入
竞态条件
,如果两个线程都进入了if
,其实后续的操作都是直接put
。也就是说,
竞态条件
下,其实返回的是两个值,而不是同一个对象。回到外层
复制代码1
2
3
4
5constant = constants.putIfAbsent(name, tempConstant); if (constant == null) { return tempConstant; }
也就是说,
竞态条件
下,为null
的就直接返回刚创建的对象。因为即使是先进入、先创建对象的线优先进入了
竞太条件
内部,却不能够保证优先写入。我们需要唯一
key
指向的唯一的对象,如果直接返回,就不能够保证同key
值的单一value
对象。正所谓成王败寇,谁先写入了,谁就是皮卡丘。
更多信息可以看这个,还有这个。
有哪些
option | type |
---|---|
CONNECT_TIMEOUT_MILLIS | Integer |
MAX_MESSAGES_PER_READ | Integer |
WRITE_SPIN_COUNT | Integer |
ALLOCATOR | ByteBufAllocator |
RCVBUF_ALLOCATOR | RecvByteBufAllocator |
AUTO_READ | Boolean |
AUTO_CLOSE | Boolean |
WRITE_BUFFER_HIGH_WATER_MARK | Integer |
WRITE_BUFFER_LOW_WATER_MARK | Integer |
WRITE_BUFFER_WATER_MARK | WriteBufferWaterMark |
MESSAGE_SIZE_ESTIMATOR | MessageSizeEstimator |
SINGLE_EVENTEXECUTOR_PER_GROUP | Boolean |
想加一个description
的,发现懂得太少,不丢人现眼了。
attr
做什么
1
2
3
4
5
6
7
8synchronized (attrs) { for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) { @SuppressWarnings("unchecked") AttributeKey<Object> key = (AttributeKey<Object>) e.getKey(); channel.attr(key).set(e.getValue()); } }
1
2channel.attr(key).set(e.getValue());
还是挨个赋值
是什么
1
2
3
4
5
6
7
8
9
10
11
12
13public 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
6serverBootstrap.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
32public <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
3final Map<ChannelOption<?>, Object> options = options0(); final Map<AttributeKey<?>, Object> attrs = attrs0();
1
2
3
4
5
6
7final Map<ChannelOption<?>, Object> options0() { return options; } final Map<AttributeKey<?>, Object> attrs0() { return attrs; }
类文件申明
1
2
3private 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
7protected 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来源小结内容请搜索靠谱客的其他文章。
发表评论 取消回复