本文使用环境版本是
netty 4.1.36.Final
jdk 1.8
经查,问题的关键点事childOption和option的问题,找了几个答案如下:
option主要是设置的ServerChannel的一些选项,而childOption主要是设置的ServerChannel的子Channel的选项。 如果是Bootstrap的话,只会有option而没有childOption,所以设置的是客户端Channel的选项。
option主要是针对boss线程组,child主要是针对worker线程组
The parameters that we set using ServerBootStrap.option apply to the ChannelConfig of a newly created ServerChannel, i.e., the server socket which listens for and accepts the client connections.
These options will be set on the Server Channel when bind() or connect() method is called. This channel is one per server.
And the ServerBootStrap.childOption applies to to a channel’s channelConfig which gets created once the serverChannel accepts a client connection.
This channel is per client (or per client socket).
So ServerBootStrap.option parameters apply to the server socket (Server channel) that is listening for connections and ServerBootStrap.
childOption parameters apply to the socket that gets created once the connection is accepted by the server socket.
The same can be extended to attr vs childAttr and handler vs childHandler methods in the ServerBootstrap class.
How could I know which option should be an option and which should be a childOption ?Which ChannelOptions are supported depends on the channel type we are using.
You can refer to the API docs for the ChannelConfig that you’re using.
netty.io/4.0/api/io/netty/channel/ChannelConfig.html
and its sub classes. You should find Available Options section for each ChannelConfig.
在设置netty服务端的option时,设置了如下参数。
server.group(boss,worker)//设置时间循环对象,前者用来处理accept事件,后者用于处理已经建立的连接的io
//Server是NioServerSocketChannel 客户端是NioSocketChannel绑定了jdk NIO创建的ServerSocketChannel对象,
//用它来建立新accept的连接
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,1024)//
// 第2次握手服务端向客户端发送请求确认,同时把此连接放入队列A中,
// 然后客户端接受到服务端返回的请求后,再次向服务端发送请求,表示准备完毕,此时服务端收到请求,把这个连接从队列A移动到队列B中,
// 此时A+B的总数,不能超过SO_BACKLOG的数值,满了之后无法建立新的TCP连接,2次握手后和3次握手后的总数
// 当服务端从队列B中按照FIFO的原则获取到连接并且建立连接[ServerSocket.accept()]后,B中对应的连接会被移除,这样A+B的数值就会变小
//此参数对于程序的连接数没影响,会影响正在准备建立连接的握手。
.option(ChannelOption.SO_KEEPALIVE,true)
//启用心跳,双方TCP套接字建立连接后(即都进入ESTABLISHED状态),
// 并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活,TCP会自动发送一个活动探测数据报文
.option(ChannelOption.TCP_NODELAY,true)
//TCP协议中,TCP总是希望每次发送的数据足够大,避免网络中充满了小数据块。
// Nagle算法就是为了尽可能的发送大数据快。
// TCP_NODELAY就是控制是否启用Nagle算法。
// 如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true关闭Nagle算法;
// 如果要减少发送次数减少网络交互,就设置为false等累积一定大小后再发送。默认为false。
.option(ChannelOption.SO_REUSEADDR,true)//是否允许重复绑定端口,重复启动,会把端口从上一个使用者上抢过来
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,30000)//连接超时30000毫秒
.option(ChannelOption.SO_TIMEOUT,5000)//输入流的read方法被阻塞时,接受数据的等待超时时间5000毫秒,抛出SocketException
//child是在客户端连接connect之后处理的handler,不带child的是在客户端初始化时需要进行处理的
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)//缓冲池
.childHandler(new ChannelInitializer() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast("decoder",new DotDecodeHandler());
}
});
在启动的时候除了警告如下
五月 30, 2019 10:38:41 上午 io.netty.bootstrap.AbstractBootstrap setChannelOption
警告: Unknown channel option 'SO_KEEPALIVE' for channel '[id: 0x3372ab6d]'
五月 30, 2019 10:38:41 上午 io.netty.bootstrap.AbstractBootstrap setChannelOption
警告: Unknown channel option 'TCP_NODELAY' for channel '[id: 0x3372ab6d]'
五月 30, 2019 10:38:41 上午 io.netty.bootstrap.AbstractBootstrap setChannelOption
警告: Unknown channel option 'SO_TIMEOUT' for channel '[id: 0x3372ab6d]'
经过跟踪调试发现点在于NETTY版本问题。
在setOption的时候,首先会判断JDK版本,然后根据1.7为分界线,去set。
在从NioOption中查找key-value对,如果没有找到,就会去JDK的Option中去查找,如果仍然没找到,那么就会报出警告。如下:
io.netty.bootstrap. AbstractBootstrap
private static void setChannelOption(Channel channel, ChannelOption> option, Object value, InternalLogger logger) {
try {
if (!channel.config().setOption(option, value)) {
logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
}
} catch (Throwable var5) {
logger.warn("Failed to set channel option '{}' with value '{}' for channel '{}'", new Object[]{option, value, channel, var5});
}
}
io.netty.channel.socket.nio. NioServerSocketChannel
public boolean setOption(ChannelOption option, T value) {
//此处根据jdk版本去执行不同的set方法
return PlatformDependent.javaVersion() >= 7 && option instanceof NioChannelOption ? NioChannelOption.setOption(this.jdkChannel(), (NioChannelOption)option, value) : super.setOption(option, value);
}
io.netty.channel.socket. DefaultServerSocketChannelConfig
public boolean setOption(ChannelOption option, T value) {
this.validate(option, value);
if (option == ChannelOption.SO_RCVBUF) {
this.setReceiveBufferSize((Integer)value);
} else if (option == ChannelOption.SO_REUSEADDR) {
this.setReuseAddress((Boolean)value);
} else {
if (option != ChannelOption.SO_BACKLOG) {
return super.setOption(option, value); //如果没有找到,去父类找
}
this.setBacklog((Integer)value);
}
return true;
}
io.netty.channel. DefaultChannelConfig
public boolean setOption(ChannelOption option, T value) {
this.validate(option, value);
//在此处没有找到我设置的SO_KEEPALIVE/TCP_NODELAY/SO_TIMEOUT
if (option == ChannelOption.CONNECT_TIMEOUT_MILLIS) {
this.setConnectTimeoutMillis((Integer)value);
} else if (option == ChannelOption.MAX_MESSAGES_PER_READ) {
this.setMaxMessagesPerRead((Integer)value);
} else if (option == ChannelOption.WRITE_SPIN_COUNT) {
this.setWriteSpinCount((Integer)value);
} else if (option == ChannelOption.ALLOCATOR) {
this.setAllocator((ByteBufAllocator)value);
} else if (option == ChannelOption.RCVBUF_ALLOCATOR) {
this.setRecvByteBufAllocator((RecvByteBufAllocator)value);
} else if (option == ChannelOption.AUTO_READ) {
this.setAutoRead((Boolean)value);
} else if (option == ChannelOption.AUTO_CLOSE) {
this.setAutoClose((Boolean)value);
} else if (option == ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK) {
this.setWriteBufferHighWaterMark((Integer)value);
} else if (option == ChannelOption.WRITE_BUFFER_LOW_WATER_MARK) {
this.setWriteBufferLowWaterMark((Integer)value);
} else if (option == ChannelOption.WRITE_BUFFER_WATER_MARK) {
this.setWriteBufferWaterMark((WriteBufferWaterMark)value);
} else if (option == ChannelOption.MESSAGE_SIZE_ESTIMATOR) {
this.setMessageSizeEstimator((MessageSizeEstimator)value);
} else {
if (option != ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP) {
//如果都没有找到则返回false,层层抛出,在log处打印出警告
return false;
}
this.setPinEventExecutorPerGroup((Boolean)value);
}
return true;
}
然后我使用低版本的netty(小于等于 4.1.7.Final ),不会报出警告,而且在处理setOption的时候有不同:
io.netty.bootstrap. AbstractBootstrap
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = this.channelFactory.newChannel();
//此处初始化channel
this.init(channel);
} catch (Throwable var3) {
if (channel != null) {
channel.unsafe().closeForcibly();
}
return (new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).setFailure(var3);
}
ChannelFuture regFuture = this.config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
io.netty.bootstrap. ServerBootstrap
void init(Channel channel) throws Exception {
Map, Object> options = this.options0();
synchronized(options) {
//开始设置option
channel.config().setOptions(options);
}
Map, Object> attrs = this.attrs0();
synchronized(attrs) {
Iterator i$ = attrs.entrySet().iterator();
while(true) {
if (!i$.hasNext()) {
break;
}
Entry, Object> e = (Entry)i$.next();
AttributeKey
io.netty.channel. DefaultChannelConfig
public boolean setOptions(Map, ?> options) {
if (options == null) {
throw new NullPointerException("options");
} else {
boolean setAllOptions = true;
Iterator i$ = options.entrySet().iterator();
while(i$.hasNext()) {
Entry, ?> e = (Entry)i$.next();
//此处循环set,如果this.setOption返回false,那么setAllOptions就是false,接下来我们进入this.setOption
if (!this.setOption((ChannelOption)e.getKey(), e.getValue())) {
setAllOptions = false;
}
}
return setAllOptions;
}
}
io.netty.channel.socket. DefaultServerSocketChannelConfig
public boolean setOption(ChannelOption option, T value) {
this.validate(option, value);
if (option == ChannelOption.SO_RCVBUF) {
this.setReceiveBufferSize((Integer)value);
} else if (option == ChannelOption.SO_REUSEADDR) {
this.setReuseAddress((Boolean)value);
} else {
if (option != ChannelOption.SO_BACKLOG) {
//此处找到不到SO_KEEPALIVE等参数后,会向上从父类中查询
return super.setOption(option, value);
}
this.setBacklog((Integer)value);
}
return true;
}
io.netty.channel. DefaultChannelConfig
发现也没有SO_KEEPALIVE选项,返回false
但是在返回后,没有地方记录这个false,而且也没有地方打印log
public boolean setOption(ChannelOption option, T value) {
this.validate(option, value);
if (option == ChannelOption.CONNECT_TIMEOUT_MILLIS) {
this.setConnectTimeoutMillis((Integer)value);
} else if (option == ChannelOption.MAX_MESSAGES_PER_READ) {
this.setMaxMessagesPerRead((Integer)value);
} else if (option == ChannelOption.WRITE_SPIN_COUNT) {
this.setWriteSpinCount((Integer)value);
} else if (option == ChannelOption.ALLOCATOR) {
this.setAllocator((ByteBufAllocator)value);
} else if (option == ChannelOption.RCVBUF_ALLOCATOR) {
this.setRecvByteBufAllocator((RecvByteBufAllocator)value);
} else if (option == ChannelOption.AUTO_READ) {
this.setAutoRead((Boolean)value);
} else if (option == ChannelOption.AUTO_CLOSE) {
this.setAutoClose((Boolean)value);
} else if (option == ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK) {
this.setWriteBufferHighWaterMark((Integer)value);
} else if (option == ChannelOption.WRITE_BUFFER_LOW_WATER_MARK) {
this.setWriteBufferLowWaterMark((Integer)value);
} else if (option == ChannelOption.WRITE_BUFFER_WATER_MARK) {
this.setWriteBufferWaterMark((WriteBufferWaterMark)value);
} else if (option == ChannelOption.MESSAGE_SIZE_ESTIMATOR) {
this.setMessageSizeEstimator((MessageSizeEstimator)value);
} else {
if (option != ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP) {
return false;
}
this.setPinEventExecutorPerGroup((Boolean)value);
}
return true;
}