概述
位置
回顾一下原来追到的位置
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
的操作,也是option
和attr
的设置,这个就不重复。涉及到的
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
。
而key
是ChannelOption
形式,又是通过key-value
的形式,用String
唯一确定一个ChannelOption
。
并保存在currentHashMap
当中,画个图梳理一下
一般的设置属性其实都是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; }
关于唯一性实现总有这么一个阶段
直接设置
map
查询
currentHashMap
查询(多线程环境)防
竞态条件
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
对象。正所谓成王败寇,谁先写入了,谁就是皮卡丘。
更多信息可以看这个,还有这个。
有哪些
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
做什么
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来源小结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复