3

有时,当通过 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 的单独类中,以便它作为自己的线程运行并且不会阻塞调用代码。但是,我担心为我维护的每个套接字连接创建线程所需的开销,特别是当 99% 的时间 SocketChannel.write() 在第一次尝试时成功,这意味着不需要线程存在. (换句话说,只有在使用 while() 循环时才真正需要将 send() 放在单独的线程中——仅在存在缓冲区问题的情况下,可能有 1% 的时间)如果存在缓冲区问题只有 1% 的时间,我不会

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

4

6 回答 6

1

The more I read about Java NIO, the more it gives me the willies. Anyway, I think this article answers your problem...

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

It sounds like this guy has a more elegant solution than the sleep loop.

Also I'm fast coming to the conclusion that using Java NIO by itself is too dangerous. Where I can, I think I'll probably use Apache MINA which provides a nice abstraction above Java NIO and its little 'surprises'.

于 2010-02-24T20:40:00.227 回答
1

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

请参阅权威的 Java NIO 书籍和这篇关于 Java的文章以开始使用。但是请注意,这是一个复杂的主题,它仍然会给您的代码带来一些多线程问题。谷歌“非阻塞 NIO”了解更多信息。

于 2009-07-18T16:10:39.253 回答
0

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

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

于 2009-07-18T16:16:24.030 回答
0

对于数百个连接,您可能不需要为 NIO 操心。好的老式阻塞套接字和线程会帮你。

使用 NIO,您可以注册对OP_WRITE选择键的兴趣,当有空间写入更多数据时,您会收到通知。

于 2009-07-18T17:22:27.110 回答
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循环
于 2009-07-18T17:44:17.440 回答
0

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

Orielly Java NIO 一书拥有所有这些。
另外: http ://www.exampledepot.com/egs/java.nio/NbServer.html?l=rel

一些在线研究让我相信 NIO 是相当过分的,除非你有很多传入的连接。否则,如果它只是一些大的传输——那么就使用一个写线程。它可能会有更快的反应。许多人对 NIO 的响应速度没有他们想要的那么快有疑问。由于您的写入线程本身是阻塞的,因此不会伤害您。

于 2009-07-18T17:44:39.517 回答