最近在阅读netty(3.2.7)源码的时候,看到服务器端接受客户端连接代码的时候嗅到了一丝不和谐的气味,具体代码见:
org.jboss.netty.channel.socket.nio.NioServerSocketPipelineSink类里面的BOSS线程:
public void run() {
final Thread currentThread = Thread.currentThread();
channel.shutdownLock.lock();
try {
for (;;) {
try {
if (selector.select(1000) > 0) {
selector.selectedKeys().clear();
}
SocketChannel acceptedSocket = channel.socket.accept();
if (acceptedSocket != null) {
registerAcceptedChannel(acceptedSocket, currentThread);
}
} catch (SocketTimeoutException e) {
// Thrown every second to get ClosedChannelException
// raised.
} catch (CancelledKeyException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedSelectorException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedChannelException e) {
// Closed as requested.
break;
} catch (Throwable e) {
logger.warn(
"Failed to accept a connection.", e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// Ignore
}
}
}
} finally {
channel.shutdownLock.unlock();
closeSelector();
}
}
1、selector.selectedKeys().clear();不管当前有多少个SelectionKey被触发,都一律清除掉,然后
2、SocketChannel acceptedSocket = channel.socket.accept();只接受一个链接。
曾今阅读别人对于NETTY的性能测试的时候貌似有说到在客户端高并发请求访问会出现丢包现象
今天看到这段代码怀疑就是这段代码的问题,稍微修改代码成如下来验证:
public void run() {
final Thread currentThread = Thread.currentThread();
channel.shutdownLock.lock();
try {
for (;;) {
try {
/**
* 由于Runnable没有简单的sleep,所以我这里使用很土的方式让线程暂停30秒再去执行selector操作。
* 测试如果服务器启动后,30秒内有三个客户端连接请求过来,服务器端会处理几次.
*/
Long currentTime = System.currentTimeMillis();
while(true){
if(System.currentTimeMillis() - currentTime>30000){
break;
}
}
if (selector.select(1000) > 0) {
selector.selectedKeys().clear();
}
SocketChannel acceptedSocket = channel.socket.accept();
if (acceptedSocket != null) {
registerAcceptedChannel(acceptedSocket, currentThread);
}
} catch (SocketTimeoutException e) {
// Thrown every second to get ClosedChannelException
// raised.
} catch (CancelledKeyException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedSelectorException e) {
// Raised by accept() when the server socket was closed.
} catch (ClosedChannelException e) {
// Closed as requested.
break;
} catch (Throwable e) {
logger.warn(
"Failed to accept a connection.", e);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// Ignore
}
}
}
} finally {
channel.shutdownLock.unlock();
closeSelector();
}
}
可以直接使用netty中的例子DiscardServer和DiscardClient来做试验。开启一个DiscardServer后,然后在30秒内直接连续启动四个DiscardClient,等30秒后发现服务器端只接受并分配了一个链接.
-------------------------------------------------------------------分割线----------------------------------------------------------------------------------------
然后我去下了最稳定发布版本的netty源码,最新的稳定版截止到本文撰写时间为(3.6.5版),在这个版本中服务器端的Boss类的代码被抽出来形成了NioServerBoss.其中的代码为:
protected void process(Selector selector) {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
if (selectedKeys.isEmpty()) {
return;
}
for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
SelectionKey k = i.next();
i.remove();
NioServerSocketChannel channel = (NioServerSocketChannel) k.attachment();
try {
// accept connections in a for loop until no new connection is ready
for (;;) {
SocketChannel acceptedSocket = channel.socket.accept();
if (acceptedSocket == null) {
break;
}
registerAcceptedChannel(channel, acceptedSocket, thread);
}
} catch (CancelledKeyException e) {
// Raised by accept() when the server socket was closed.
k.cancel();
channel.close();
} catch (SocketTimeoutException e) {
// Thrown every second to get ClosedChannelException
// raised.
} catch (ClosedChannelException e) {
// Closed as requested.
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to accept a connection.", t);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
// Ignore
}
}
}
}
在这个版本会根据SelectionKey的个数来来接受客户端的请求,这个版本我未做过性能测试,应该可以解决客户端高并发下的丢包现象.
PS:最新更新到4.0的版本的netty代码又发生了翻天覆地的变化,还是直接去阅读4.0的代码到时候.
在研究Netty 3.2.7源码时,发现NioServerSocketPipelineSink的BOSS线程可能引发高并发环境下客户端连接丢包。代码中清除了所有SelectionKey并仅接受一个连接。通过修改代码进行测试,证实了该问题。在后续的Netty 3.6.5版本中,服务器端的Boss代码进行了改进,根据SelectionKey数量处理客户端请求,可能已解决丢包问题。

3万+

被折叠的 条评论
为什么被折叠?



