SocketChannel.write()的Java NIO线程问题

时间:2009-07-18 15:55:54

标签: java sockets nio

有时,在通过SocketChannel.write()发送大量数据时,底层TCP缓冲区会被填满,我必须不断重新尝试write(),直到数据全部发送完毕。

所以,我可能会有这样的事情:

public void send(ByteBuffer bb, SocketChannel sc){
   sc.write(bb);
   while (bb.remaining()>0){
      Thread.sleep(10);
      sc.write(bb);          
   }
}

问题在于,大型ByteBuffer和溢出的底层TCP缓冲区的偶然问题意味着对send()的此调用将阻塞意外的时间。在我的项目中,有数百个客户端同时连接,一个套接字连接引起的一个延迟可以使整个系统爬行,直到解决了一个SocketChannel的延迟。当发生延迟时,它可能会导致项目其他区域的连锁反应减慢,并且具有低延迟非常重要。

我需要一个解决方案,它将透明地处理这个TCP缓冲区溢出问题,并且在需要多次调用SocketChannel.write()时不会导致阻塞所有内容。我已经考虑将send()放入一个扩展Thread的单独类中,因此它作为自己的线程运行,并且不会阻塞调用代码。但是,我担心为我正在维护的EACH套接字连接创建一个线程所需的开销,特别是当99%的时候,SocketChannel.write()在第一次尝试时成功,这意味着线程不需要在那里。 (换句话说,只有在使用while()循环时才需要将send()放在一个单独的线程中 - 仅在存在缓冲区问题的情况下,可能是1%的时间)如果存在缓冲区问题只有1%的时间,我不需要一个线程的开销来为其他99%的send()调用。

我希望这有道理......我真的可以使用一些建议。谢谢!

7 个答案:

答案 0 :(得分:1)

在Java NIO之前, 每个插槽使用一个Thread来获得良好的性能。这是所有基于套接字的应用程序的问题,而不仅仅是Java。为了克服这个问题,为所有操作系统添加了对非阻塞IO的支持。 Java NIO实现基于Selectors

请参阅The definitive Java NIO book和此On Java文章以开始使用。但请注意,这是一个复杂的主题,它仍然会在代码中引入一些多线程问题。谷歌“非阻止NIO”获取更多信息。

答案 1 :(得分:1)

你不需要sleep(),因为write会立即返回或阻塞。 如果第一次没有写入,你可以有一个传递写入的执行程序。 另一种选择是使用一个小的线程池来执行写操作。

但是,最好的选择可能是使用选择器(如建议的那样),以便您知道套接字何时可以执行另一次写入。

答案 2 :(得分:0)

对于数百个连接,您可能不需要打扰NIO。好的老式阻塞插座和线程都可以帮到你。

使用NIO,您可以在OP_WRITE注册选择键,并在有空间写入更多数据时收到通知。

答案 3 :(得分:0)

假设您已经使用了循环,那么您需要做一些事情 Selector.select();确定哪些插槽已准备好进行I / O.

  • 在创建套接字通道后将其设置为非阻塞,sc.configureBlocking(false);
  • 写入(可能是部分)缓冲区并检查是否还有其他内容。缓冲区本身负责当前位置以及剩余多少。

这样的东西
sc.write(bb);
if(sc.remaining() == 0)
   //we're done with this buffer, remove it from the select set if there's nothing else to send.
else
    //do other stuff/return to select loop
  • 摆脱睡觉的while循环

答案 4 :(得分:0)

我现在面临一些相同的问题:
- 如果你有少量的连接,但有大量的传输,我只会创建一个线程池,并让写入阻止写入线程。
- 如果你有很多连接,那么你可以使用完整的Java NIO,并在你的accept()ed套接字上注册OP_WRITE,然后等待选择器进入。

Orielly Java NIO的书就是这一切 也: http://www.exampledepot.com/egs/java.nio/NbServer.html?l=rel

网上的一些研究让我相信NIO相当过分,除非你有很多传入的连接。否则,如果只是几个大转移 - 那么只需使用写线程。它可能会有更快的反应。许多人对NIO的问题并没有像他们想要的那样快速地进行复制。由于你的写线程是自己阻止它不会伤害你。

答案 5 :(得分:0)

我第二次回答有关在选择键上注册OP_WRITE的答案。

我会推荐Rox Java NIO Tutorial

答案 6 :(得分:0)

我读到的关于Java NIO的内容越多,它就会给我带来更多的好处。无论如何,我认为这篇文章可以解答你的问题...

http://weblogs.java.net/blog/2006/05/30/tricks-and-tips-nio-part-i-why-you-must-handle-opwrite

听起来这个家伙比睡眠循环有更优雅的解决方案。

此外,我很快得出结论,单独使用Java NIO太危险了。在我可以的地方,我想我可能会使用Apache MINA,它提供了一个比Java NIO更好的抽象和它的“惊喜”。