【问题标题】:Non-blocking socket client and selectors非阻塞套接字客户端和选择器
【发布时间】:2020-10-30 06:34:00
【问题描述】:

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

选择器支持基于键的、非阻塞的、多路复用的 I/O。换句话说,选择器使您能够通过多个通道执行 I/O。 (Java - 完整参考

作为一个流,TCP 消息始终是有序的,并且我只会打开一个套接字连接(单个SocketChannel),那么使用Selectors 有什么意义呢?我认为没有意义,对吗?

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

【问题讨论】:

  • 它主要在服务器端有用,它必须处理许多同时连接。

标签: java sockets nio


【解决方案1】:

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

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

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

NIO使用selectors基本解决了这个问题。

【讨论】:

  • 谢谢!因此,在我的客户案例中,带有读取器线程和写入器线程的普通阻塞 Socket 可能是最直接的解决方案。
  • 是的。我认为我在解释这一点时有点过分了,因为您在客户端,不需要为许多连接提供服务。所以阻塞连接应该没问题。
  • 你的答案是完美的不用担心,因为解释也可以应用于客户,因为它仍然是一个 SelectableChannel
猜你喜欢
  • 2017-04-18
  • 2017-01-29
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
  • 1970-01-01
  • 2019-04-02
  • 1970-01-01
  • 2021-02-15
相关资源
最近更新 更多