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

时间:2020-07-09 15:43:16

标签: java sockets nio

由于我想尝试与Telegram服务器建立基本的TCP连接(使用MTProto),因此我开始阅读有关Java NIO类的信息。但是,我为尝试理解Selector对于客户的意义而感到“困惑”。

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

由于始终将TCP消息作为流进行排序,并且我将只打开一个套接字连接(单个SocketChannel),所以使用Selector有什么意义?我认为没有意义,对吗?

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

1 个答案:

答案 0 :(得分: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解决了这个问题。