SocketChannel.write() 的 Java NIO 线程问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1147873/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java NIO Threading issue with SocketChannel.write()
提问by DivideByHero
Sometimes, while sending a large amount of data via SocketChannel.write(), the underlying TCP buffer gets filled up, and I have to continually re-try the write() until the data is all sent.
有时,当通过 SocketChannel.write() 发送大量数据时,底层 TCP 缓冲区被填满,我必须不断重试 write() 直到数据全部发送。
So, I might have something like this:
所以,我可能有这样的事情:
public void send(ByteBuffer bb, SocketChannel sc){
sc.write(bb);
while (bb.remaining()>0){
Thread.sleep(10);
sc.write(bb);
}
}
The problem is that the occasional issue with a large ByteBuffer and an overflowing underlying TCP buffer means that this call to send() will block for an unexpected amount of time. In my project, there are hundreds of clients connected simultaneously, and one delay caused by one socket connection can bring the whole system to a crawl until this one delay with one SocketChannel is resolved. When a delay occurs, it can cause a chain reaction of slowing down in other areas of the project, and having low latency is important.
问题在于,大 ByteBuffer 和溢出的底层 TCP 缓冲区偶尔会出现问题,这意味着对 send() 的调用将阻塞一段意外的时间。在我的项目中,有数百个客户端同时连接,一个socket连接造成的一个延迟可以让整个系统陷入困境,直到解决这个一个SocketChannel的延迟。当发生延迟时,可能会导致项目其他区域变慢的连锁反应,因此低延迟很重要。
I need a solution that will take care of this TCP buffer overflow issue transparently and without causing everything to block when multiple calls to SocketChannel.write() are needed. I have considered putting send() into a separate class extending Thread so it runs as its own thread and does not block the calling code. However, I am concerned about the overhead necessary in creating a thread for EACH socket connection I am maintaining, especially when 99% of the time, SocketChannel.write() succeeds on the first try, meaning there's no need for the thread to be there. (In other words, putting send() in a separate thread is really only needed if the while() loop is used -- only in cases where there is a buffer issue, perhaps 1% of the time) If there is a buffer issue only 1% of the time, I don't need the overhead of a thread for the other 99% of calls to send().
我需要一个解决方案,它可以透明地处理这个 TCP 缓冲区溢出问题,并且在需要多次调用 SocketChannel.write() 时不会导致所有内容阻塞。我已经考虑将 send() 放入一个扩展 Thread 的单独类中,以便它作为自己的线程运行并且不会阻塞调用代码。但是,我担心为我维护的每个套接字连接创建线程所需的开销,尤其是在 99% 的情况下, SocketChannel.write() 在第一次尝试时成功,这意味着线程不需要在那里. (换句话说,只有在使用 while() 循环时才真正需要将 send() 放在单独的线程中——仅在存在缓冲区问题的情况下,可能是 1% 的时间)如果存在缓冲区问题只有 1% 的时间,我不
I hope that makes sense... I could really use some suggestions. Thanks!
我希望这是有道理的...我真的可以使用一些建议。谢谢!
回答by AngerClown
Prior to Java NIO, you hadto use one Thread per socket to get good performance. This is a problem for all socket based applications, not just Java. Support for non-blocking IO was added to all operating systems to overcome this. The Java NIO implementation is based on Selectors.
在 Java NIO 之前,您必须为每个套接字使用一个线程以获得良好的性能。这是所有基于套接字的应用程序的问题,而不仅仅是 Java。所有操作系统都添加了对非阻塞 IO 的支持来克服这个问题。Java NIO 实现基于Selectors.
See The definitive Java NIO bookand this On Javaarticle to get started. Note however, that this is a complex topic and it still brings some multithreading issues into your code. Google "non blocking NIO" for more information.
请参阅权威的 Java NIO 书籍和这篇On Java文章以开始使用。但是请注意,这是一个复杂的主题,它仍然会给您的代码带来一些多线程问题。谷歌“非阻塞 NIO”了解更多信息。
回答by geme_hendrix
The more I read about Java NIO, the more it gives me the willies. Anyway, I think this article answers your problem...
我对 Java NIO 了解得越多,它就越给我勇气。无论如何,我认为这篇文章可以解决您的问题...
http://weblogs.java.net/blog/2006/05/30/tricks-and-tips-nio-part-i-why-you-must-handle-opwrite
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'.
此外,我很快得出结论,单独使用 Java NIO 太危险了。如果可以,我想我可能会使用 Apache MINA,它在 Java NIO 及其小“惊喜”之上提供了一个很好的抽象。
回答by Peter Lawrey
You don't need the sleep() as the write will either return immediately or block. You could have an executor which you pass the write to if it doesn't write the first time. Another option is to have a small pool of thread to perform the writes.
您不需要 sleep() 因为写入将立即返回或阻塞。如果第一次没有写入,您可以拥有一个执行程序,您可以将写入传递给它。另一种选择是使用一个小的线程池来执行写入。
However, the best option for you may be to use a Selector (as has been suggested) so you know when a socket is ready to perform another write.
但是,对您来说最好的选择可能是使用选择器(如已建议的那样),以便您知道套接字何时准备好执行另一次写入。
回答by Tom Hawtin - tackline
For hundreds of connections, you probably don't need to bother with NIO. Good old fashioned blocking sockets and threads will do you.
对于数百个连接,您可能不需要为 NIO 烦恼。好的老式阻塞套接字和线程可以为您服务。
With NIO, you can register interest in OP_WRITEfor the selection key, and you will get notified when there is room to write more data.
使用NIO,您可以注册对OP_WRITE选择键的兴趣,当有空间写入更多数据时您会收到通知。
回答by nos
There are a few things you need to do, assuming you already have a loop using Selector.select(); to determine which sockets are ready for I/O.
假设您已经有一个使用 Selector.select(); 的循环,您需要做一些事情;以确定哪些套接字已准备好进行 I/O。
- Set the socket channel to non-blocking after you've created it, sc.configureBlocking(false);
- Write (possibly parts of) the buffer and check if there's anything left. The buffer itself takes care of current position and how much is left.
- 创建套接字通道后将其设置为非阻塞,sc.configureBlocking(false);
- 写入(可能是部分)缓冲区并检查是否还有剩余内容。缓冲区本身负责处理当前位置以及还剩多少。
Something like
就像是
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
- Get rid of your while loop that sleeps
- 摆脱睡眠的 while 循环
回答by EdH
I am facing some of the same issues right now:
- If you have a small amount of connections, but with large transfers, I would just create a threadpool, and let the writes block for the writer threads.
- If you have a lot of connections then you could use full Java NIO, and register OP_WRITE on your accept()ed sockets, and then wait for the selector to come in.
我现在面临一些相同的问题:
- 如果您有少量连接,但传输量很大,我只会创建一个线程池,并让写入器线程的写入阻塞。
- 如果你有很多连接,那么你可以使用完整的 Java NIO,并在你的 accept()ed 套接字上注册 OP_WRITE,然后等待选择器进来。
The Orielly Java NIO book has all this.
Also:
http://www.exampledepot.com/egs/java.nio/NbServer.html?l=rel
Orielly Java NIO 一书有这一切。
另外:http: //www.exampledepot.com/egs/java.nio/NbServer.html?l=
rel
Some research online has led me to believe NIO is pretty overkill unless you have a lot of incoming connections. Otherwise, if its just a few large transfers - then just use a write thread. It will probably have quicker response. A number of people have issues with NIO not repsonding as quick as they want. Since your write thread is on its own blocking it wont hurt you.
一些在线研究让我相信 NIO 是非常矫枉过正的,除非你有很多传入的连接。否则,如果它只是几个大的传输 - 那么只需使用写线程。它可能会有更快的反应。许多人对 NIO 没有像他们想要的那样迅速做出回应有问题。因为你的写线程是在它自己的阻塞中它不会伤害你。

