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);
        }

  

  方法步骤如下:

  1. 判断 outboundBuffer 有效性。
  2. filterOutboundMessage 将 ByteBuf 过滤成池化或者线程局部直接内存(如果不是直接内存的话),如果不是ByteBuf就是直接返回
  3. 预估当前 ByteBuf 大小(就是可读字节数)。
  4. 将 ByteBuf 包装成一个 Entry 节点放入到 outboundBuffer 的单向链表中。
   outboundBuffer.addMessage 方法会另外用一篇文章分析,现在只需要知道调用 channel.writeAndFlush 并不会直接发送出去,而是缓存到

  AbstractChannel的一个缓存里  ChannelOutboundBuffer 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;
            }
        }

    }

 

 

posted on 2021-01-20 11:05  MaXianZhe  阅读(330)  评论(0)    收藏  举报

导航