一、引言
接着上一篇,数据在 pipline 中处理完成后,往往需要给予客户端一定的响应,这个阶段就是本篇的主要内容:发送数据。
二、Netty 写数据的三种方式
快递场景(包裹) | Netty 写数据(数据) |
---|---|
揽收到仓库 | write : 写到一个 buffer |
从仓库发货 | flush : 把 buffer 中的数据发送出去 |
揽收到仓库并立即发货(加急件) | writeAndFlush : 写到 buffer 立即发送 |
揽收与发货之间有中转仓库 | write 和 flush 之间有个 ChannelOutBoundBuffer |
三、Netty 写数据要点
WriteBufferWaterMark.high()
四、Netty 写数据过程一览
写数据分为两个部分:
-
write:
将数据写到 Buffer ,调用
ChannelOutboundBuffer#addMessage()
方法将消息放到待写队列中的队尾,形成链表结构。 -
flush:
发送 Buffer 里面的数据,调用
AbstractChannel.AbstractUnsafe#flush()
ChannelOutboundBuffer#addFlush() NioSocketChannel#dowrite()
五、Netty 发送数据过程源码分析
我们继续以 netty-example
模块下的 EchoServer
为例,分析 Netty 发送数据的过程。
这里 EchoServer
的例子做的是,客户端不断的发送递增的数字给服务端,服务端接收后再反向发送给客户端,所以我们从 EchoServerHandler#channelRead()
方法入手,看服务端如何发送数据。
1、write 部分
在 channelRead()
方法中调用了 AbstractChannelHandlerContext#invokeWrite()
:
private void invokeWrite0(Object msg, ChannelPromise promise) { try { ((ChannelOutboundHandler) handler()).write(this, msg, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } }
实际到后面是调用了 AbstractChannel 的 AbstractUnsafe#write()
方法:
public final void write(Object msg, ChannelPromise promise) { assertEventLoop(); ChannelOutboundBuffer outboundBuffer = this.outboundBuffer; if (outboundBuffer == null) { // If the outboundBuffer is null we know the channel was closed and so // need to fail the future right away. If it is not null the handling of the rest // will be done in flush0() // See https://github.com/netty/netty/issues/2362 safeSetFailure(promise, newClosedChannelException(initialCloseCause, "write(Object, ChannelPromise)")); // release message now to prevent resource-leak ReferenceCountUtil.release(msg); return; } int size; try { msg = filterOutboundMessage(msg); size = pipeline.estimatorHandle().size(msg); if (size < 0) { size = 0; } } catch (Throwable t) { safeSetFailure(promise, t); ReferenceCountUtil.release(msg); return;