Netty源码剖析之发送数据
本文参考自 https://www.jianshu.com/p/787073ff5348
一 调用链路
通常在使用Netty发送消息的时候都是这么做的,入参是一个Object,什么类型都可以。但是一般都是ByteBuf或者String
channel.writeAndFlush(ClientIdleStateTrigger.HEART_BEAT);
跟着源代码看看调用链
AbstractChannel.writeAndFlush
public ChannelFuture writeAndFlush(Object msg) { return pipeline.writeAndFlush(msg); }
继续看pipeline
DefaultChannelPipeline.writeAndFlush
public final ChannelFuture writeAndFlush(Object msg) { return tail.writeAndFlush(msg); }
看到这正好说明写出是从tail到head的顺序写的
AbstractChannelHandlerContext.writeAndFlush
public ChannelFuture writeAndFlush(Object msg) { return writeAndFlush(msg, newPromise()); }
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { if (msg == null) { throw new NullPointerException("msg"); } if (isNotValidPromise(promise, true)) { ReferenceCountUtil.release(msg); // cancelled return promise; } write(msg, true, promise); return promise; }
private void write(Object msg, boolean flush, ChannelPromise promise) { AbstractChannelHandlerContext next = findContextOutbound();//findContextOutbound里的逻辑就是找tail的前面的outHandler final Object m = pipeline.touch(msg, next);//touch不用管,Netty会管理释放堆外内存,如果我们只是传入一个String经过该方法出来还是String EventExecutor executor = next.executor(); if (executor.inEventLoop()) { if (flush) { next.invokeWriteAndFlush(m, promise); } else { next.invokeWrite(m, promise); } } else { AbstractWriteTask task; if (flush) { task = WriteAndFlushTask.newInstance(next, m, promise); } else { task = WriteTask.newInstance(next, m, promise); } safeExecute(executor, task, promise, m); } }
上面的代码区别是执行逻辑是否在当前的EventLoop中,如果不在就会把这个数据包装成任务放到当前的EventLoop中,
逻辑也不复杂,简单说下
AbstractWriteTask.run()
public final void run() { try { ChannelOutboundBuffer buffer = ctx.channel().unsafe().outboundBuffer(); // Check for null as it may be set to null if the channel is closed already if (ESTIMATE_TASK_SIZE_ON_SUBMIT && buffer != null) { buffer.decrementPendingOutboundBytes(size); } write(ctx, msg, promise); } finally { // Set to null so the GC can collect them directly ctx = null; msg = null; promise = null; handle.recycle(this); } }
最后执行的还是 ctx.invokeWrite(msg, promise);
重点还是看看 AbstractChannelHandlerContext.invokeWriteAndFlush
private void invokeWriteAndFlush(Object msg, ChannelPromise promise) { if (invokeHandler()) { invokeWrite0(msg, promise); invokeFlush0(); } else { writeAndFlush(msg, promise); } }
private void invokeWrite0(Object msg, ChannelPromise promise) { try { ((ChannelOutboundHandler) handler()).write(this, msg, promise);//还是会遍历handler链表调用每个outHandler } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } }
HeadContext.write
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); }
最终调用的就是unsafe.write
二 unsafe.write
AbstractUnsafe.write 这里多提一句 AbstractUnsafe是AbstractChannel的内部类
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, WRITE_CLOSED_CHANNEL_EXCEPTION); // 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; } outboundBuffer.addMessage(msg, size, promise); }
方法步骤如下:
- 判断 outboundBuffer 有效性。
- filterOutboundMessage 将 ByteBuf 过滤成池化或者线程局部直接内存(如果不是直接内存的话),如果不是ByteBuf就是直接返回
- 预估当前 ByteBuf 大小(就是可读字节数)。
- 将 ByteBuf 包装成一个 Entry 节点放入到 outboundBuffer 的单向链表中。
三 unSafe.flush()
AbstractChannel.AbstractUnsafe.flush
public final void flush() { assertEventLoop(); ChannelOutboundBuffer outboundBuffer = this.outboundBuffer; if (outboundBuffer == null) { return; } outboundBuffer.addFlush(); flush0(); }
addFlush 方法将刚刚添加进出站 buffer 的数据进行检查,并准备写入 Socket。flush0 做真正的写入操作,其中,调用了 JDK 的 Socket 的 write 方法,将 ByteBuf 封装的 ByteBuffer 写到 Socket 中。
protected void flush0() { if (!this.inFlush0) { ChannelOutboundBuffer outboundBuffer = this.outboundBuffer; if (outboundBuffer != null && !outboundBuffer.isEmpty()) { this.inFlush0 = true; if (!AbstractChannel.this.isActive()) { try { if (AbstractChannel.this.isOpen()) { outboundBuffer.failFlushed(AbstractChannel.FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true); } else { outboundBuffer.failFlushed(AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false); } } finally { this.inFlush0 = false; } } else { try { AbstractChannel.this.doWrite(outboundBuffer); } catch (Throwable var11) { if (var11 instanceof IOException && AbstractChannel.this.config().isAutoClose()) { this.close(this.voidPromise(), var11, AbstractChannel.FLUSH0_CLOSED_CHANNEL_EXCEPTION, false); } else { outboundBuffer.failFlushed(var11, true); } } finally { this.inFlush0 = false; } } } } }
最后又到了 AbstractChannel.this.doWrite
实际调用 NioSocketChannel.doWrite
protected void doWrite(ChannelOutboundBuffer in) throws Exception { while(true) { int size = in.size(); if (size == 0) { this.clearOpWrite(); break; } long writtenBytes; boolean done; boolean setOpWrite; writtenBytes = 0L; done = false; setOpWrite = false; ByteBuffer[] nioBuffers = in.nioBuffers(); int nioBufferCnt = in.nioBufferCount(); long expectedWrittenBytes = in.nioBufferSize(); java.nio.channels.SocketChannel ch = this.javaChannel(); int i; label51: switch(nioBufferCnt) { case 0: super.doWrite(in); return; case 1: ByteBuffer nioBuffer = nioBuffers[0]; i = this.config().getWriteSpinCount() - 1; while(true) { if (i < 0) { break label51; } int localWrittenBytes = ch.write(nioBuffer); if (localWrittenBytes == 0) { setOpWrite = true; break label51; } expectedWrittenBytes -= (long)localWrittenBytes; writtenBytes += (long)localWrittenBytes; if (expectedWrittenBytes == 0L) { done = true; break label51; } --i; } default: for(i = this.config().getWriteSpinCount() - 1; i >= 0; --i) { long localWrittenBytes = ch.write(nioBuffers, 0, nioBufferCnt); if (localWrittenBytes == 0L) { setOpWrite = true; break; } expectedWrittenBytes -= localWrittenBytes; writtenBytes += localWrittenBytes; if (expectedWrittenBytes == 0L) { done = true; break; } } } in.removeBytes(writtenBytes); if (!done) { this.incompleteWrite(setOpWrite); break; } } }
浙公网安备 33010602011771号