在EchoServerHandler
中打两个断点
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 读取数据后写回客户端
ctx.write(msg); // 在这里打个断点
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush(); // 在这里打个断点
}
}
ctx.write(msg)
并不会立即把数据发送出去,ctx.flush()
才会把数据发送出去。
abstract class AbstractChannelHandlerContext implements ChannelHandlerContext, ResourceLeakHint {
// ①
@Override
public ChannelFuture write(Object msg) {
return write(msg, newPromise());
}
@Override
public ChannelPromise newPromise() {
return new DefaultChannelPromise(channel(), executor());
}
// ②
@Override
public ChannelFuture write(final Object msg, final ChannelPromise promise) {
// 第二个参数为flush,这里传入的是false
// 也就是默认不进行真正地发送
write(msg, false, promise);
return promise;
}
// ③
private void write(Object msg, boolean flush, ChannelPromise promise) {
ObjectUtil.checkNotNull(msg, "msg");
// 异常校验......省略
// 寻找下一个可用的ChannelOutboundHandler类型的ChannelHandlerContext
// 在ChannelPipeline中也就是prev指针标记的那个
// 这里找到的就是LoggingHandler对应的那个ChannelHandlerContext
final AbstractChannelHandlerContext next = findContextOutbound(flush ?
(MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
// 这里的m其实就是msg
final Object m = pipeline.touch(msg, next);
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
if (flush) {
next.invokeWriteAndFlush(m, promise);
} else {
// flush为false,所以走到了这里
// 调用context的invokeWrite()方法
next.invokeWrite(m, promise);
}
} else {
final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
if (!safeExecute(executor, task, promise, m, !flush)) {
task.cancel();
}
}
}
// ④
void invokeWrite(Object msg, ChannelPromise promise) {
if (invokeHandler()) {
// 走的是这里
invokeWrite0(msg, promise);
} else {
write(msg, promise);
}
}
// ⑤
private void invokeWrite0(Object msg, ChannelPromise promise) {
try {
// 调用Handler的write()方法
((ChannelOutboundHandler) handler()).write(this, msg, promise);
} catch (Throwable t) {
notifyOutboundHandlerException(t, promise);
}
}
}
然后会走到LoggingHandler
的write
方法
public class LoggingHandler extends ChannelDuplexHandler {
// ⑥
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "WRITE", msg));
}
// 又调回了第②步中的方法, 继续寻找下一个可用的ChannelOutboundHandler类型的ChannelHandlerContext
ctx.write(msg, promise);
}
}
可以知道,写数据在ChannelPipeline
中的传递,也是通过ChannelHandlerContext
进行,
每次寻找下一个可用的ChannelOutboundHandler
类型的ChannelHandlerContext
,调用它里面的ChannelHandler
的write()
方法。
然后,在ChannelHandler
里面调用ctx.write(msg, promise)
才能让这个链往下传递,继续寻找下一个可用的ChannelOutboundHandler
类型的ChannelHandlerContext
。
对于接收数据,如果需要数据在ChannelPipeline
中传递,就调用ctx.fireChannelRead(msg)
方法;
对于写出数据,如果需要数据在ChannelPipeline
中传递,就调用ctx.write(msg)
或者ctx.write(msg, promise)
方法。
通过上面的分析,最后一定会走到ChannelPipeline
的HeadContext
的write()
方法,在这个方法中打一个断点:
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
// unsafe 为 io.netty.channel.socket.nio.NioSocketChannel.NioSocketChannelUnsafe
unsafe.write(msg, promise);
}
}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void write(Object msg, ChannelPromise promise) {
assertEventLoop();
// 出站缓存
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) {
try {
// release message now to prevent resource-leak
ReferenceCountUtil.release(msg);
} finally {
safeSetFailure(promise,
newClosedChannelException(initialCloseCause, "write(Object, ChannelPromise)"));
}
return;
}
int size;
try {
// 过滤消息
msg = filterOutboundMessage(msg);
// 计算消息大小
size = pipeline.estimatorHandle().size(msg);
if (size < 0) {
size = 0;
}
} catch (Throwable t) {
try {
ReferenceCountUtil.release(msg);
} finally {
safeSetFailure(promise, t);
}
return;
}
// 消息添加到缓存
outboundBuffer.addMessage(msg, size, promise);
}
}
}
public final class ChannelOutboundBuffer {
public void addMessage(Object msg, int size, ChannelPromise promise) {
// 将msg封装成一个Entry
Entry entry = Entry.newInstance(msg, size, total(msg), promise);
// 这里就是一个单链表的添加过程了
if (tailEntry == null) {
flushedEntry = null;
} else {
Entry tail = tailEntry;
tail.next = entry;
}
tailEntry = entry;
if (unflushedEntry == null) {
unflushedEntry = entry;
}
incrementPendingOutboundBytes(entry.pendingSize, false);
}
}
因此,ctx.write()
方法最终只是把消息添加到了一个叫做ChannelOutboundBuffer
的缓存中,并没有真正地发送出去。
ctx.flush()
的调用过程跟ctx.write(msg)
是类似的,直接来到HeadContext
的flush()
方法。
public class DefaultChannelPipeline implements ChannelPipeline {
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
@Override
public void flush(ChannelHandlerContext ctx) {
// unsafe 为 io.netty.channel.socket.nio.NioSocketChannel.NioSocketChannelUnsafe
unsafe.flush();
}
}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
@Override
public final void flush() {
assertEventLoop();
// ctx.write(msg)就是把数据写到this.outboundBuffer里面的
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) {
return;
}
outboundBuffer.addFlush();
flush0();
}
}
}
public abstract class AbstractNioChannel extends AbstractChannel {
protected abstract class AbstractNioUnsafe extends AbstractUnsafe implements NioUnsafe {
@Override
protected final void flush0() {
// Flush immediately only when there's no pending flush.
// If there's a pending flush operation, event loop will call forceFlush() later,
// and thus there's no need to call it now.
if (!isFlushPending()) {
super.flush0();
}
}
}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
protected abstract class AbstractUnsafe implements Unsafe {
protected void flush0() {
// 省略......
final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
// 省略......
try {
doWrite(outboundBuffer);
} catch (Throwable t) {
handleWriteError(t);
} finally {
inFlush0 = false;
}
}
}
}
最后进入到NioSocketChannel
的doWrite
方法
public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel {
@Override
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
// ch 为 java.nio.channels.SocketChannel
SocketChannel ch = javaChannel();
int writeSpinCount = config().getWriteSpinCount();
do {
if (in.isEmpty()) {
clearOpWrite();
return;
}
// Ensure the pending writes are made of ByteBufs only.
int maxBytesPerGatheringWrite = ((NioSocketChannelConfig) config).getMaxBytesPerGatheringWrite();
// ①从ChannelOutboundBuffer中取出ByteBuffer
// 前面分析write()的时候放里面放的实际是ByeBuf
// 因为ByteBuf实际上是对ByteBuffer的包装
// 所以这里取出来的时候直接就转换成ByteBuffer了
ByteBuffer[] nioBuffers = in.nioBuffers(1024, maxBytesPerGatheringWrite);
// 获取缓存了几条数据
int nioBufferCnt = in.nioBufferCount();
// Always use nioBuffers() to workaround data-corruption.
// See https://github.com/netty/netty/issues/2761
switch (nioBufferCnt) {
case 0:
// We have something else beside ByteBuffers to write so fallback to normal writes.
writeSpinCount -= doWrite0(in);
break;
case 1: {
// 我们只写了一条数据 所以会走到这里来
ByteBuffer buffer = nioBuffers[0];
int attemptedBytes = buffer.remaining();
// ② 调用SocketChannel的write()方法写出数据
final int localWrittenBytes = ch.write(buffer);
if (localWrittenBytes <= 0) {
incompleteWrite(true);
return;
}
adjustMaxBytesPerGatheringWrite(attemptedBytes, localWrittenBytes, maxBytesPerGatheringWrite);
in.removeBytes(localWrittenBytes);
--writeSpinCount;
break;
}
default: {
// Zero length buffers are not added to nioBuffers by ChannelOutboundBuffer, so there is no need
// to check if the total size of all the buffers is non-zero.
// We limit the max amount to int above so cast is safe
long attemptedBytes = in.nioBufferSize();
// ② 调用SocketChannel的write()方法写出数据
final long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt);
if (localWrittenBytes <= 0) {
incompleteWrite(true);
return;
}
// Casting to int is safe because we limit the total amount of data in the nioBuffers to int above.
adjustMaxBytesPerGatheringWrite((int) attemptedBytes, (int) localWrittenBytes,
maxBytesPerGatheringWrite);
in.removeBytes(localWrittenBytes);
--writeSpinCount;
break;
}
}
} while (writeSpinCount > 0);
incompleteWrite(writeSpinCount < 0);
}
}
这里有两个关键方法:
先从ChannelOutboundBuffer
中取出ByteBuffer
再通过Java
原生的SocketChannel
写出数据
调用ctx.write()
方法时,只是把数据添加到ChannelOutboundBuffer
缓存中
调用ctx.flush()
方法时,才把数据从ChannelOutboundBuffer
取出来
调用Java
原生的SocketChannel
把数据发送出去