2

当发送不受控制的 TextWebSocketFrames 到一个简单的 Netty WebSocket 回显服务器实现(示例包中的实现的略微修改版本)时,无需等待 ChannelFuture 上的客户端同步(),服务器上的堆内存使用量正在增长以指数方式直到它最终耗尽内存。

测试用例:客户端不会等到实际字节被写入,也不会等待服务器在写入下一个文本帧之前将文本“回显”回客户端

for (int i = 0; i < 10000000; i++) {
    ch.write(new TextWebSocketFrame("Message #" + i));
}

在观察 yourkit 内存分析器时(测试开始 15 秒,写入大约 20.000 帧),BigEndianHeapChannelBuffer 对象的数量已经过度增长。

BigEndianHeapChannelBuffer 284,509 (objects) 9,104,288 (shallow size) (after ~30.000 frames sent within 10 second window)

在服务器端,可以从 BigEndianHeapChannelBuffers 和 CompositeChannelBuffers 对象中观察到大量堆积,这些对象从未被清理过,也不会被垃圾收集(由于引用被持有,这可能是不可能的)。我猜这与(单个)工作线程无法向客户端通道写入下游“回声”响应有关,因为它正忙于处理来自客户端的快速传入请求

有没有办法在服务器端防止/限制这种情况(意外拒绝服务)?

4

1 回答 1

4

在 Netty 中,大多数 I/O 操作都是异步的。因此,在不等待之前的写入完成的情况下写入数千条消息将使您获得OutOfMemoryError. 为避免这种情况,我更喜欢使用计数器变量来计算挂起的写入次数。例如:

private final AtomicInteger pendingWrites = new AtomicInteger();

...
while (pendingWrites.get() < MAX_PENDING_WRITES) {
  pendingWrites.incrementAndGet();
  ch.write(msg).addListener(new ChannelFutureListener() {
    ...
    pendingWrites.decrementAndGet();
    if (pendingWrites.get() < MAX_PENDING_WRITES) {
      // resume writing here
    }
  }
}

或者,您可以使用ChunkedWriteHandlerwhich 基本上完成相同的工作。

于 2012-06-15T01:40:39.683 回答