非阻塞套接字客户端和选择器

Non-blocking socket client and selectors

由于我想尝试实现与 Telegram 服务器的基本 TCP 连接(使用 MTProto),我开始阅读有关 Java NIO 类 的内容。但是,我在试图理解 Selectors 对客户的意义时“卡住了”。

A selector supports key-based, non-blocking, multiplexed I/O. In other words, selectors enable you to perform I/O through multiple channels. (Java - The complete reference)

由于 TCP 消息作为流总是有序的,而且我只会打开一个套接字连接(一个 SocketChannel),使用 Selectors 有什么意义?我觉得没有意义吧?

如果我的自我回答是正确的,为什么不直接使用阻塞 I/O?

NIO基本上是用在服务器端处理大规模的。我将尝试解释典型服务器的工作原理。

服务器有一个请求队列,轮询线程从中消耗连接作为 blocking dequeue 操作(在 Java 中,请求队列的默认长度为 50。这意味着如果您尝试在请求队列已满时启动第 51 个连接,您将得到 ConnectionRefused 异常)。

一个典型的 blocking 实现是这样的:

  1. 服务器接受连接并将其置于 requests queue

  2. 轮询 thread 从队列头部消耗连接并将其分派给 thread pool。如果 thread-pool 队列未满并继续使用来自 queue.

    的连接,则轮询线程变为空闲
  3. 在某些时候,线程池中的所有线程都将变得繁忙,并且轮询线程将在向池提交更多连接时被阻塞(因为线程池队列是 blocking queue).

  4. requests queue 同时开始填充。在某些时候,它会变得完全满,服务器将不再接受任何连接。

此时,我们的服务器无法再扩展了。请注意,池中的“繁忙”线程可能根本不忙,而只是被阻塞 - 比如说在它们所服务的相应套接字的 InputStream 上获取更多数据。

现在考虑这个设计:

  1. 轮询线程使用请求队列头部的项目。

  2. 它把它放在一个无限列表中。

  3. 另一个线程不断迭代此列表并检查套接字上是否发生了任何 activity(读到读、准备好写等)。如果有 activity,则提供 socket。请注意,这些 socketsNIO 模式下运行。也就是说,如果没有activity,我们的线程就不会被阻塞。

  4. 轮询线程同时继续向列表提交连接,因为列表是无界的。它不会在任何地方被阻塞(除非它正在等待请求队列上的新连接)。

在上面的设计中,请注意我们的规模仅受我们系统资源的限制——即 list 持有的连接数。响应时间会受到影响,因为只有一个线程为所有连接提供服务。由于无意识的迭代,您 CPU 的消耗将非常高。但与之前的设计不同,您仍然可以连接到服务器。

NIO基本上用selectors.

解决了这个问题