java.nio vs每个socket的新线程

我正在开发一个1到多个服务器客户端应用程序,这是一个小项目。

由于套接字IO阻塞。 我正在为此寻找解决方案。

谁能告诉我这两种解决方案的优劣是什么?

  1. 使用java.nio
  2. 每个连接的客户端都有一个新的线程。

谢谢

这两种方法都没有错。 如果你有有限的客户端,第二个选项就足够了(甚至可能基于多核架构而茁壮成长),否则让java.nio管理你的资源可能是有益的。

在同一主题以及其他post上看到这个问题 ,或者为什么不考虑这篇博文 ,主张反对在大多数情况下使用java.nio。

个别线程

  • 您可以使用简单的InputStream / OutputStream(或Reader / Writer)API,并将Streams包装在一起。 在我目前的项目中 ,我正在使用一堆

    • MessageFormatter,一个自定义格式类
    • 为PrintWriter
    • OutputStreamWriter
    • DebugOutputStream(自定义类,用于调试目的的副本)
    • DeflatorOutputStream(自定义子类,实际上支持刷新)
    • SSLSocket的OutputStream

    而另一方面是接收方。 这很舒服,因为你只需要处理程序逻辑中的顶层(其余的主要是每个构造函数调用)。

  • 每个连接都需要一个新的线程(甚至是一对线程,具体取决于架构)。

    (在我的项目中,我为每个连接都有一个MessageParser-Thread,然后将各个作业提供给ThreadPool,然后这些作业可以写入一个或多个打开的连接,而不仅仅是生成它们的连接。写入是同步的, 当然。)

  • 每个线程需要相当多的堆栈空间,如果您在资源有限的机器上,这可能会有问题。

在短期连接的情况下,实际上您不希望每个连接都有一个新线程,但只需要在ThreadPool上执行新的Runnable,因为新线程的构造需要一些时间。

非阻塞IO

  • 如果你有这样一个有多次转换的多层架构,你必须自己安排。 有可能的:

    • 我的MessageFormatter可以写入CharBuffer以及Writer。
    • 对于Char-to-Byte格式化,使用CharsetEncoder / CharsetDecoder(在CharBuffer和ByteBuffer之间传输数据)。
    • 对于压缩,我在Deflater / Inflater周围使用了包装类,它在两个ByteBuffers之间传输数据。
    • 对于加密,我使用了SSLEngine(每个连接一个),它也使用ByteBuffers进行输入和输出。
    • 然后,写入SocketChannel(或从另一个方向读取)。

    尽管如此,管理开销仍然很麻烦,因为您必须跟踪数据的位置。 (我实际上每个连接都有两个管道,一个或两个线程管理所有管道中的数据,在这两个线程之间等待选择器上的套接字上的新数据(或者在那里的新空间,在传出的情况下)。解析消息后的实际处理仍然发生在ThreadPool中的生成的Runnables中。)

  • 您只需要少量线程。 这实际上是我试图异步执行此操作的原因。 它工作(使用相同的同步客户端),但比我的multithreading解决方案慢,所以我把它放回去,直到我们的内存耗尽太multithreading。 (到现在为止,同时没有那么多连接。)

NIO获胜,我可以在我想要的时候实际停止线程。 学习NIO现在很困难,但是正确使用缓冲区几乎从未在任何地方解释过。 其中一个原因,我相信,人们不能为了exrta的利益而挤压NIO。

NIO的其他部分是大多数开发人员通常无法编写和使用状态机,因此他们最终会过多地复制缓冲区。

NIO的性能是合理的,并且应该使用不超过#cores线程,乘以应用程序的IO因子,其中IO因子是等待磁盘IO完成的应用程序的百分比。

原因很简单。 当你有#cores worker时,每个worker可能被绑定到一个cpu核心并且可以将它最小化。 工作人员越多,上下文切换越多,这正是您不想要的,以及您首先使用NIO的原因。

如果工作人员必须等待IO,他们可以处理其他请求,因此使用比核心更多的工作人员来获得完整的CPU利用率。

如果使用线程,则可以获得以下优势:

  • 您可以在ThreadLocals中存储会话信息。
  • 您不必以其他方式管理会话信息。

我试过Apache MINA ,真的很棒。 我强烈推荐它。