我正在使用 ws
模块在 NodeJS 上实现 WebSockets 服务器。服务器应每分钟向所有客户端发送一次更新。我已经实现了此功能,但我对其在客户端连接可能停止的情况下的功能有些担忧。
我担心当与客户端的连接变得不活动时会发生什么,例如由于网络连接以不发送 TCP RST 或 FIN 的方式中断。
令我有些惊讶的是,在 async
方法中,没有使用 await
关键字调用 send()
方法。 send()
方法是否只是对所有要发送的数据进行排队?如果套接字缓冲区已满怎么办,send()
是否会以一种方式阻塞,从而导致被阻塞客户端以外的其他客户端饥饿?
如果 send()
永远不会阻塞,如果数据排队、排队、排队会发生什么......?它可以使用不断增加的无限量的内存吗?
理想情况下,如果上次更新尚未完全发送,我想省略发送每分钟一次的更新。我可以使用 ws
模块实现此目的吗?
最佳答案
I'm concerned of what happens when a connection to the client becomes inactive, for example due to network connection breaking in a way that doesn't send a TCP RST or FIN.
如果连接以这种方式丢失(可能是由于客户端系统被关闭或物理断开),那么服务器上的 TCP 将检测到断开的连接,因为它将不会收到已发送数据的确认。 TCP 可能需要几分钟才能放弃,但在这种情况下,这听起来并不是一个大问题。
最坏的情况是客户端系统保持连接但客户端进程停止从连接读取数据。在这种情况下,发送的数据将在客户端累积,直到客户端的套接字接收缓冲区填满,然后发送的数据将在服务器累积 - 首先在内核套接字发送缓冲区中,然后在服务器进程内存中。
I'm somewhat surprised that the send() method is not called with the await keyword in an async method.
ws
早于 async/await 和 Promise 多年。我想象 API 最终会被改进,但这还没有发生。
Does the send() method just queue all data to be sent? What if the socket buffers become full, can send() block in a way that causes starvation of other clients than the blocked one?
WebSocket.send
最终调用内置 Net
模块的 Socket.write
。 (请参阅 https://github.com/websockets/ws/blob/master/lib/sender.js 底部的 sendFrame
函数了解该调用,并参阅 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
的数据写入内核时,该调用将触发回调。如果您的服务器在回调触发之前不向该客户端发送更多数据,则该连接在用户空间中缓冲的数据量将被限制为接近最近发送
的大小。 (它不会精确地达到这个大小,因为缓冲的数据将包括 WebSocket 帧,如果您的连接已加密,则还包括 SSL 帧和填充,位于传递给send
的原始数据之上。)或者在准备在该连接上
发送
数据之前,检查连接Socket
的bufferSize
属性。 bufferSize 指示当前在该 Socket 的用户空间中缓冲的数据量。如果它非零,则跳过该客户端的发送
。
关于node.js - NodeJS WebSockets (ws) 模块是否实现了背压?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55188328/