欢迎关注Github:https://github.com/teaey/
### 背景
银时跟我讲,想从
Netty3
迁移到
Netty4
。
问其原因是因为
Netty3
在容器里会报错,错误堆栈:
java.io.IOException: 无法立即完成一个非阻止性套接字操作。
at sun.nio.ch.SocketDispatcher.close0(Native Method)
at sun.nio.ch.SocketDispatcher.preClose(SocketDispatcher.java:44)
at sun.nio.ch.SocketChannelImpl.implCloseSelectableChannel(SocketChannelImpl.java:677)
at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:201)
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:97)
### 分析
看到这个问题,之前我也没有遇到过,不过如果
netty3
有这个问题,
netty4
应该也会存在。那就看看到底什么导致这个问题。
找到
SocketDispatcher
的
close0
方法,这是个本地方法:
找到
Windows
的实现:
Windows
平台通过调用
closesocket
(
winsock2.h
)关闭套接字。
if no error occurs, closesocket returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling
WSAGetLastError
.
这里很明显是发生出错误向上抛出了异常。
于是回过头去看下代码,果然,图为截取的
Netty
代码片段:
巨硬的文档是这么说的:
Setting the l_onoff member of the
linger
structure to nonzero and the l_linger member with a nonzero timeout interval on a nonblocking socket is not recommended.
意思是,在非阻塞的
Socket
情况下不建议设置
SO_LINGER
参数。
In this case, the call to closesocket will fail with an error of
WSAEWOULDBLOCK
if the close operation cannot be completed immediately. If closesocket fails with
WSAEWOULDBLOCK
the socket handle is still valid, and a disconnect is not initiated. The application must call closesocket again to close the socket.
如果设置了
SO_LINGER
,并且制定了超时时间,这时,我们调用
closesocket
方法,方法不能立即完成的话,会抛出
WSAEWOULDBLOCK
错误。但是,这个
socket
此时还是有效的,可以一段时间之后再次调用
close
方法进行关闭尝试。
### 解决方法
最终,解决方法去掉
这行代码。
改进之后,在调用
close
方法时,不会抛出异常并且在底层
socket
关闭前,系统会尽可能的把将缓冲队列的数据发送给对端。原文如下:
If the l_onoff member of the
LINGER
structure is zero on a stream socket, the closesocket call will return immediately and does not receive whether the socket is blocking or nonblocking. However, any data queued for transmission will be sent, if possible, before the underlying socket is closed.
### 总结
在使用
NIO
的时候,最好不要配置
SO_LINGER
,如果设置了该参数,在
close
的时候如缓冲区有数据待写出,会抛出
IOException
。
后记:最近银时发现,Zookeeper之前的版本也是有设置这个参数,并且在最新版本去掉了这个参数,难道大神们的代码也是Ctrl+C,Ctrl+V过来的。呵呵。
### 参考资料