在参考我之前的问题here时,我试图弄清楚将大型对象从服务器流式传输到客户端的正确方法。
服务器正在执行write
后跟flush
,并且Channel
中有一个处理程序,它将获取大对象并迭代地将其分解为小块,执行{{1} 1}}在每个块上。我希望这会将大对象变成客户可以根据需要消费和重建的可管理消息流。
我看到的是每个都位于出站缓冲区中,直到它们都被写入并且可以被发送,或者我是OOM服务器。我希望他们在写完时能够刷新/发送以避免这个问题。 (如果我没有OOM服务器,它会像设计的流一样进入客户端,这就是阻止任何数据的传输,直到所有数据被写入)。
如果我尝试通过writeAndFlush
向Callable
发送与Executor
关联的ChannelHandlerContext
进行分解操作,然后将原始ChannelPromise
标记为成功,我仍然看到同样的行为。
如果我使用非网络Executor
,我似乎得到了正确的行为,我可以看到数据立即登陆客户端,但这感觉就像是错误的解决方案。
当将大对象分解为较小的可写对象时,我确实看到在写入第一个对象后,通道可写性变为false。我意识到,正如@ norman-maurer指出的那样,我应该在此时停止写作。但目前尚不清楚如何获得可以安全恢复写作的事件。在这种情况下你也不清楚如何处理写作,但也许这是因为我看不出你如何能够得到这个事件。
答案 0 :(得分:4)
听起来你的第一次尝试是循环这样的事情
while(moreToSend) {
channel.writeAndFlush(...)
moreToSend = checkFinished();
}
听起来你在通道的IO线程中这样做,这是将数据写入套接字的线程。线程在处理循环时可能无法写入数据。 Netty的唯一选择是对写入进行排队。
我对Netty 4.x(仍然是3.x)并不完全熟悉,但听起来你的第二次尝试也是如此。通过将Callable调度到与ChannelHandlerContext
相关联的执行程序,然后,除非您将处理程序包装在另一个执行程序中,否则实际上是要求在IO线程上执行Callable并且同样的问题也适用。
诺曼说你应该致电writeAndFlush
,直到channel.isWritable
返回false。当Netty引发channelWritabilityChanged
并且channel.isWritable
返回true时,您可以继续写作。注意channelWritabilityChanged
上引发了ChannelInboundHandler
,这意味着您的处理程序需要同时为ChannelInboundHandler
和ChannelOutboundHandler
(假设它已经是出站处理程序)。
或者,您可以编写第一个块并使用返回的ChannelFuture
注册一个侦听器,而不是一次尝试尽可能多地写入。当回调operationComplete
时,如果future.isSuccess
返回true,则以相同的方式写下一个块。这样,当前一个块被刷新到OS发送缓冲区时,您将写入块。如果您需要将大对象与其他流量混合发送,它也应该可以正常工作。