使用Java NIO进行10000并发连接

我使用Java nio编写了一个服务器(类似于一个)和客户端代码 。

我正在努力实现尽可能多的连接。 从之前的建议开始,我放慢了客户端创建的过程,为操作系统(Windows 8)提供了足够的时间来处理请求。

我在不同的机器上运行客户端代码,以便Server具有所有可用的运行空间。

当我尝试创建10,000个连接时,大约8500个连接并且rest被拒绝连接,并且拒绝连接客户端(客户端代码中的线程)发生的更多(稍后在客户端代码中进行循环)。

我的CPU和内存使用率非常高。我看到大多数(占总CPU消耗的48%)被select方法消耗(主要由gui事件rest)。 是因为这么多客户? 我也看到有些人在JRE7中抱怨这个错误,并建议使用JRE6。

对于javaw.exe进程,内存使用率为2000+ MB。(我注意到1个进程使用了​​低内存但主要CPU使用率。)当所有8500个左右的客户端连接时,总体使用率约为98%。 系统也多次绞死但继续服务。我看到非页面池内存使用量在这个过程中从178 MB增加到310 MB(最大限制是多少?)。是不是因为当我们写入套接字时非页面汇集内存使用?

任何人都可以告诉我可能会遇到哪些限制因此10,000次成功连接是不可能的? (每个进程限制的套接字?)(非分页内存?)(Backlog Queue再次?)调整可能允许限制被推送? (Windows机器)

我在4GB系统上使用Windows 8。

`

 public class Server implements Runnable { public final static String ADDRESS = "192.168.2.14"; public final static int PORT = 8511; public final static long TIMEOUT = 10000; public int clients; ByteBuffer readBuffer = ByteBuffer.allocate(1024); private ServerSocketChannel serverChannel; private Selector selector; private Map dataTracking = new HashMap(); public Server(){ init(); } private void init(){ System.out.println("initializing server"); if (selector != null) return; if (serverChannel != null) return; try { selector = Selector.open(); serverChannel = ServerSocketChannel.open(); serverChannel.configureBlocking(false); serverChannel.socket().bind(new InetSocketAddress(ADDRESS, PORT)); serverChannel.register(selector, SelectionKey.OP_ACCEPT); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { System.out.println("Now accepting connections..."); try{ while (!Thread.currentThread().isInterrupted()){ int ready = selector.select(); if(ready==0) continue; Iterator keys = selector.selectedKeys().iterator(); while (keys.hasNext()){ SelectionKey key = keys.next(); keys.remove(); if (!key.isValid()){ continue; } if (key.isAcceptable()){ System.out.println("Accepting connection"); accept(key); } if (key.isWritable()){ System.out.println("Writing..."); write(key); } if (key.isReadable()){ System.out.println("Reading connection"); read(key); } } } } catch (IOException e){ e.printStackTrace(); } finally{ closeConnection(); } } private void write(SelectionKey key) throws IOException{ SocketChannel channel = (SocketChannel) key.channel(); byte[] data = dataTracking.get(channel); dataTracking.remove(channel); **int count = channel.write(ByteBuffer.wrap(data)); if(count == 0) { key.interestOps(SelectionKey.OP_WRITE); return; } else if(count > 0) { key.interestOps(0); key.interestOps(SelectionKey.OP_READ); }** } private void closeConnection(){ System.out.println("Closing server down"); if (selector != null){ try { selector.close(); serverChannel.socket().close(); serverChannel.close(); } catch (IOException e) { e.printStackTrace(); } } } private void accept(SelectionKey key) throws IOException{ ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel(); SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel == null) { throw new IOException(); } socketChannel.configureBlocking(false); clients++; **//socketChannel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ); SelectionKey skey = socketChannel.register(selector, SelectionKey.OP_READ);** byte[] hello = new String("Hello from server").getBytes(); dataTracking.put(socketChannel, hello); } private void read(SelectionKey key) throws IOException{ SocketChannel channel = (SocketChannel) key.channel(); readBuffer.clear(); int length; try { length = channel.read(readBuffer); } catch (IOException e) { System.out.println("Reading problem, closing connection"); System.out.println("No of clients :"+clients); key.cancel(); channel.close(); return; } if (length == -1){ System.out.println("Nothing was there to be read, closing connection"); channel.close(); key.cancel(); return; } readBuffer.flip(); byte[] data = new byte[1000]; readBuffer.get(data, 0, length); String fromclient = new String(data,0,length,"UTF-8"); System.out.println("Received: "+fromclient); String dat = fromclient+channel.getRemoteAddress(); data= dat.getBytes(); echo(key,data); } private void echo(SelectionKey key, byte[] data) throws IOException{ SocketChannel socketChannel = (SocketChannel) key.channel(); dataTracking.put(socketChannel, data); **//key.interestOps(SelectionKey.OP_WRITE); try { write(key); } catch(IOException e) { System.out.println("Problem in echo"+e); e.printStackTrace(); } } public static void main(String [] args) { Thread serv = new Thread(new Server()); serv.start(); } 

}

 socketChannel.register(selector, SelectionKey.OP_WRITE|SelectionKey.OP_READ); 

这是不正确的用法。 您的选择器将旋转,因为OP_WRITE几乎总是准备好,除非在套接字发送缓冲区已满的极少数情况下。 这就是为什么你没有尽可能快地处理OP_ACCEPT原因。 当你没有什么可写的时候,你正忙着处理OP_WRITE

使用OP_WRITE的正确方法如下:

  • 仅为OP_READ注册新接受的频道
  • 当你有东西写入频道时,只需写下来
  • 如果该写操作返回零,则为OP_WRITE注册通道,保存您尝试写入的ByteBuffer ,然后返回到选择循环
  • OP_WRITE在通道上触发时,使用相同的缓冲区调用write()
  • 如果写入成功但未返回零,则再次注册OP_READ ,或者至少从interestOps删除OP_WRITE

注意关闭频道取消其密钥。 您不需要取消。