Неблокирующий клиент сокета и селекторы - PullRequest
2 голосов
/ 09 июля 2020

Поскольку я хотел бы попробовать реализовать базовое c TCP-соединение с серверами Telegram (с использованием MTProto), я начал читать о классах Java NIO. Однако я «застрял», пытаясь понять смысл Selector s для клиента.

Селектор поддерживает неблокирующий мультиплексированный ввод-вывод на основе ключей. Другими словами, селекторы позволяют выполнять ввод-вывод через несколько каналов. (Java - Полная справка )

Поскольку сообщения TCP в виде потока всегда упорядочиваются, и я открою только одно соединение сокета (одно SocketChannel), какой смысл использовать Selector s? Думаю, в этом нет смысла, я прав?

Если мой ответ правильный, почему бы не использовать блокировку ввода-вывода напрямую?

1 Ответ

1 голос
/ 09 июля 2020

NIO в основном используется на стороне сервера для обработки больших объемов. Я попытаюсь объяснить, как работает типичный сервер.

На сервере есть очередь запросов, из которой поток опроса потребляет соединения как операцию blocking dequeue (в Java длина запросов по умолчанию queue - 50. Это означает, что если вы попытаетесь инициировать 51-е соединение, пока очередь запросов заполнена, вы получите исключение ConnectionRefused).

Типичная реализация blocking выглядит следующим образом:

  1. Сервер принимает соединение и помещает его в requests queue.

  2. Опрос thread потребляет соединения из заголовка очереди и отправляет его в а thread pool. Поток опроса становится свободным, если очередь thread-pool не заполнена, и продолжает использовать соединения из queue.

  3. В какой-то момент все потоки в пуле потоков станут занято, и поток опроса будет заблокирован при отправке дополнительных подключений в пул (поскольку очередь пула потоков - это blocking queue).

  4. requests queue начинает заполняться в тем временем. В какой-то момент он полностью заполнится, и сервер больше не будет принимать какие-либо соединения.

На этом этапе наш сервер просто больше не может масштабироваться. Обратите внимание, что «занятые» потоки в пуле могут быть совсем не заняты, а просто заблокированы - скажем, для получения дополнительных данных на InputStream соответствующего сокета, который они обслуживают.

Теперь рассмотрим эту схему:

  1. Поток опроса потребляет элементы из заголовка очереди запросов.

  2. Он помещает его в неограниченный список.

  3. Другой поток непрерывно просматривает этот список и проверяет, произошла ли какая-либо активность в сокете (чтение для чтения, готовность к записи и т. Д. c). Если есть активность, обслуживается socket. Обратите внимание, что эти sockets работают в режиме NIO. То есть, если нет активности, наш поток не будет заблокирован.

  4. Тем временем поток опроса продолжает отправлять подключения к списку, поскольку список неограничен. Он нигде не блокируется (кроме тех случаев, когда он ожидает нового соединения в очереди запросов).

Обратите внимание, что наш масштаб ограничен только нашими системными ресурсами. - а именно, сколько соединений держит list. Время отклика существенно снизится, поскольку все соединения обслуживает только один поток. Потребление ЦП будет довольно высоким из-за бессмысленной итерации. Но вы все равно сможете подключиться к серверу, в отличие от предыдущего дизайна.

NIO в основном решает эту проблему, используя selectors.

...