java nio 如何处理慢速的连接
java nio 如何处理慢速的连接
对企业级的服务器软件,高性能和可扩展性是基本的要求。除此之外,还应该有应对各种不同环境的能力。例如,一个好的服务器软件不应该假设所有的客户端都有很快的处理能力和很好的网络环境。如果一个客户端的运行速度很慢,或者网络速度很慢,这就意味着整个请求的时间变长。而对于服务器来说,这就意味着这个客户端的请求将占用更长的时间。这个时间的延迟不是由服务器造成的,因此CPU的占用不会增加什么,但是网络连接的时间会增加,处理线程的占用时间也会增加。这就造成了当前处理线程和其他资源得不到很快的释放,无法被其他客户端的请求来重用。例如Tomcat,当存在大量慢速连接的客户端时,线程资源被这些慢速的连接消耗掉,使得服务器不能响应其他的请求了。
前面介绍过,NIO的异步非阻塞的形式,使得很少的线程就能服务于大量的请求。通过Selector的注册功能,可以有选择性地返回已经准备好的频道,这样就不需要为每一个请求分配单独的线程来服务。
在一些流行的NIO的框架中,都能看到对OP_ACCEPT和OP_READ的处理。很少有对OP_WRITE的处理。我们经常看到的代码就是在请求处理完成后,直接通过下面的代码将结果返回给客户端:
【例17.7】不对OP_WRITE进行处理的样例:
public static long flushChannel(SocketChannel socketChannel, ByteBuffer bb, long writeTimeout) throws IOException{ SelectionKey key = null; Selector writeSelector = null; int attempts = 0; int bytesProduced = 0; try { while (bb.hasRemaining()) { int len = socketChannel.write(bb); attempts++; if (len < 0){ throw new EOFException(); } bytesProduced += len; if (len == 0) { if (writeSelector == null){ writeSelector = SelectorFactory.getSelector(); if (writeSelector == null){ // Continue using the main one continue; } } key = socketChannel.register(writeSelector, key.OP_WRITE); if (writeSelector.select(writeTimeout) == 0) { if (attempts > 2) throw new IOException("Client disconnected"); } else { attempts--; } } else { attempts = 0; } } } finally { if (key != null) { key.cancel(); key = null; } if (writeSelector != null) { // Cancel the key. writeSelector.selectNow(); SelectorFactory.returnSelector(writeSelector); } } return bytesProduced;}