Модуль NodeJS WebSockets (ws) реализует противодавление? - PullRequest
0 голосов
/ 15 марта 2019

Я реализую сервер WebSockets на NodeJS, используя модуль ws. Сервер должен отправлять раз в минуту обновление всем клиентам. Я уже реализовал это, но у меня есть некоторые опасения по поводу его функциональности в условиях, когда клиентские соединения могут зависнуть.

Меня беспокоит то, что происходит, когда соединение с клиентом становится неактивным, например, из-за разрыва сетевого соединения, который не отправляет TCP RST или FIN.

Я несколько удивлен, что метод send() не вызывается с ключевым словом await в методе async. Метод send() просто ставит в очередь все данные для отправки? Что, если буферы сокета заполнятся, может send() блокировать таким образом, что вызывает голод других клиентов, кроме заблокированных?

Если send() никогда не блокируется, что произойдет, если данные поставлены в очередь, поставлены в очередь и поставлены в очередь ...? Может ли он использовать постоянно увеличивающийся неограниченный объем памяти?

В идеале я хотел бы отказаться от отправки обновления один раз в минуту, если последнее обновление не было отправлено полностью. Можно ли этого добиться с помощью модуля ws?

1 Ответ

1 голос
/ 16 марта 2019

Я обеспокоен тем, что происходит, когда соединение с клиентом становится неактивен, например, из-за разрыва сетевого соединения таким образом, что не отправляет TCP RST или FIN.

Если соединение таким образом потеряно (возможно, из-за того, что клиентская система отключена или физически отключена), то TCP на сервере обнаружит разрыв соединения, поскольку он не будет получать подтверждения отправленных данных. Для отказа TCP может потребоваться несколько минут, но в этом случае это не кажется большой проблемой.

В худшем случае клиентская система остается подключенной, но клиентский процесс перестает считывать данные из подключения. В этом случае отправленные данные будут накапливаться на клиенте до тех пор, пока клиентский сокет не заполнит приемный буфер, а затем отправленные данные будут накапливаться на сервере - сначала в буфере отправки сокета в ядре, а затем в памяти процесса сервера.

Я несколько удивлен, что метод send () не вызывается с ключевое слово await в асинхронном методе.

ws предшествует асинхронному ожиданию и обещаниям по годам. Я полагаю, что API в конечном итоге будет модернизирован, но это еще не произошло.

Метод send () является просто очередью все данные для отправки? Что если буферы сокетов заполнятся, может блок send () таким образом, что вызывает голод других клиентов, кроме заблокирован один?

WebSocket.send в конечном итоге вызывает Socket.write встроенного Net модуля. (См. Функцию sendFrame в нижней части https://github.com/websockets/ws/blob/master/lib/sender.js для этого вызова и см. https://nodejs.org/docs/latest-v8.x/api/net.html#net_class_net_socket для документации класса Socket.)

Socket.write буферизует данные в пользовательском процессе, если ядро ​​не может немедленно принять данные. Данные буферизуются отдельно для каждого Socket, поэтому обычно эта буферизация не влияет на передачу на других Socket, подключенных к другим клиентам. Тем не менее, нет никаких ограничений на количество данных, которые один Socket будет буферизировать. В крайнем случае буферизованные данные Socket могут занимать всю память серверного процесса, и в результате сбой сервера будет мешать доставке данных всем клиентам.

Есть несколько способов избежать этой проблемы. На ум приходят два простых метода:

  • предоставляет аргумент обратного вызова завершения для вызова send. Этот обратный вызов будет передан вызову Socket.write, который вызовет обратный вызов, когда все эти данные write будут записаны в ядро. Если ваш сервер воздерживается от отправки большего количества данных этому клиенту до тех пор, пока не сработает обратный вызов, объем данных, буферизованных в пространстве пользователя для этого соединения, будет ограничен размером, близким к размеру самого последнего send. (Это не будет точно такой размер, потому что буферизованные данные будут включать кадрирование WebSocket, а также кадрирование SSL и заполнение, если ваше соединение зашифровано, поверх исходных данных, передаваемых в send.) Или

  • проверьте свойство bufferSize соединения Socket перед подготовкой к send данным по этому соединению. bufferSize указывает объем данных, которые в настоящее время буферизируются в пространстве пользователя для этого сокета. Если он ненулевой, пропустите send для этого клиента.

...