netty4.0.x源码分析—write和flush

         一般的开发者write和flush数据,都是基于ChannelHandlerContext ctx,然后调用其相应的write和flush方法。下面分别对这两个方法进行代码分析。(这里顺便提示一下,这两个方法都是从应用往底层发数据,属于OutboundHandler类型。如果没有特别的需求,我们不需要定义自己的handler,可以使用默认的Handler,这个在后面的分析中会体现。)

1、write方法

下面是ChannelHandlerContext的具体类DefaultChannelHandlerContext中write的相关代码

    @Override
    public ChannelFuture write(Object msg) {
        return write(msg, newPromise()); //构造了一个newPromise对象,调用带promise的write方法,
    }

    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }

        validatePromise(promise, true);//验证promise

        write(msg, false, promise);//再一次调用write方法,flush参数设置为false,这个参数比较重要,真正的数据发送过程是flush,后面会提到

        return promise;
    }

    private void write(Object msg, boolean flush, ChannelPromise promise) {

        DefaultChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeWrite(msg, promise);//调用invokeWrite函数
            if (flush) {
                next.invokeFlush();
            }
        } else {
            int size = channel.estimatorHandle().size(msg);
            if (size > 0) {
                ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();
                // Check for null as it may be set to null if the channel is closed already
                if (buffer != null) {
                    buffer.incrementPendingOutboundBytes(size, false);
                }
            }
            executor.execute(WriteTask.newInstance(next, msg, size, flush, promise));
        }
    }

<pre name="code" class="java">    private void invokeWrite(Object msg, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler).write(this, msg, promise);//调用handler的write方法,这里我们使用HeadHandler默认的Handler方法。
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }
 下面再看HeadHandler类的write方法 

        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            unsafe.write(msg, promise);
        }
还记得之前,我们提到的unsafe对象吗,从这个代码中,我们可以看出unsafe对象的重要性了,真正的“苦力”还是unsafe对象啊,所以不容忽视它,赶紧来看看它的实现,下面是unsafe的write方法,主要unsafe是一个接口,下面又多个扩展的具体类,这里的write方法在protected abstract class AbstractUnsafe中定义,注意它是AbstractChannel的一个内部类。下面就是write的定义。
        @Override
        public void write(Object msg, ChannelPromise promise) {
            if (!isActive()) {
                // Mark the write request as failure if the channel is inactive.
                if (isOpen()) {
                    promise.tryFailure(NOT_YET_CONNECTED_EXCEPTION);
                } else {
                    promise.tryFailure(CLOSED_CHANNEL_EXCEPTION);
                }
                // release message now to prevent resource-leak
                ReferenceCountUtil.release(msg);
            } else {
                outboundBuffer.addMessage(msg, promise);
            }
        }
通过这个代码,我们可以清楚的看到,其实write的过程,只是把数据写入了一个buffer中,并没有真正的发送。真正的发送过程是我们后面要分析的flush的过程,好吧赶紧看看flush的过程。

2、flush方法

上面讲到flush是真正的发送数据的过程,所以这个方法还是比较关键的。先来看看DefaultChannelHandlerContext中的相关代码:

    @Override
    public ChannelHandlerContext flush() {
        final DefaultChannelHandlerContext next = findContextOutbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeFlush();
        } else {
            Runnable task = next.invokeFlushTask;
            if (task == null) {
                next.invokeFlushTask = task = new Runnable() {
                    @Override
                    public void run() {
                        next.invokeFlush();
                    }
                };
            }
            executor.execute(task);
        }

        return this;
    }

    private void invokeFlush() {
        try {
            ((ChannelOutboundHandler) handler).flush(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }
这个代码逻辑还是比较清晰的,还是我们之前分析的最终调用默认的HeadHandler来处理flush,下面是HeadHandler的flush定义。

        @Override
        public void flush(ChannelHandlerContext ctx) throws Exception {
            unsafe.flush();
        }
这里又是unsafe这个苦力,在做事情。那就看看unsafe中flush的定义吧,仍然是在protected abstract class AbstractUnsafe中定义的,注意它是AbstractChannel的一个内部类。
        @Override
        public void flush() {
            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                return;
            }

            outboundBuffer.addFlush();
            flush0();
        }

        protected void flush0() {
            if (inFlush0) {
                // Avoid re-entrance
                return;
            }

            final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null || outboundBuffer.isEmpty()) {
                return;
            }

            inFlush0 = true;

            // Mark all pending write requests as failure if the channel is inactive.
            if (!isActive()) {
                try {
                    if (isOpen()) {
                        outboundBuffer.failFlushed(NOT_YET_CONNECTED_EXCEPTION);
                    } else {
                        outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION);
                    }
                } finally {
                    inFlush0 = false;
                }
                return;
            }

            try {
                doWrite(outboundBuffer);
            } catch (Throwable t) {
                outboundBuffer.failFlushed(t);
                if (t instanceof IOException) {
                    close(voidPromise());
                }
            } finally {
                inFlush0 = false;
            }
        }
逻辑仍然很简单,我们看到这里它最终是调用了doWrite函数,而且以buffer作参数,这个buffer其实就是write时数据存储的地方。write的过程这是就要和Channel打交道了,还记得之前我们分析过的channel吗,这里write定义在AbstractNioByteChannel类中(Server端以字节的形式发送数据),下面是具体的代码:

    @Override
    protected void doWrite(ChannelOutboundBuffer in) throws Exception {
        int writeSpinCount = -1;

        for (;;) {
            Object msg = in.current();
            if (msg == null) {
                // Wrote all messages.
                clearOpWrite();
                break;
            }

            if (msg instanceof ByteBuf) {
                ByteBuf buf = (ByteBuf) msg;
                int readableBytes = buf.readableBytes();
                if (readableBytes == 0) {
                    in.remove();
                    continue;
                }
                if (!buf.isDirect()) {
                    ByteBufAllocator alloc = alloc();
                    if (alloc.isDirectBufferPooled()) {
                        // Non-direct buffers are copied into JDK's own internal direct buffer on every I/O.
                        // We can do a better job by using our pooled allocator. If the current allocator does not
                        // pool a direct buffer, we rely on JDK's direct buffer pool.
                        buf = alloc.directBuffer(readableBytes).writeBytes(buf);
                        in.current(buf);
                    }
                }
                boolean done = false;
                long flushedAmount = 0;
                if (writeSpinCount == -1) {
                    writeSpinCount = config().getWriteSpinCount();
                }
                for (int i = writeSpinCount - 1; i >= 0; i --) {
                    int localFlushedAmount = doWriteBytes(buf);
                    if (localFlushedAmount == 0) {
                        break;
                    }

                    flushedAmount += localFlushedAmount;
                    if (!buf.isReadable()) {
                        done = true;
                        break;
                    }
                }

                in.progress(flushedAmount);

                if (done) {
                    in.remove();
                } else {
                    // Did not write completely.
                    setOpWrite();
                    break;
                }
            } else if (msg instanceof FileRegion) {
                FileRegion region = (FileRegion) msg;
                boolean done = false;
                long flushedAmount = 0;
                if (writeSpinCount == -1) {
                    writeSpinCount = config().getWriteSpinCount();
                }
                for (int i = writeSpinCount - 1; i >= 0; i --) {
                    long localFlushedAmount = doWriteFileRegion(region);
                    if (localFlushedAmount == 0) {
                        break;
                    }

                    flushedAmount += localFlushedAmount;
                    if (region.transfered() >= region.count()) {
                        done = true;
                        break;
                    }
                }

                in.progress(flushedAmount);

                if (done) {
                    in.remove();
                } else {
                    // Did not write completely.
                    setOpWrite();
                    break;
                }
            } else {
                throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg));
            }
        }
    }
从上面的代码逻辑,我们看到了最终write可能调用两个函数,一个是doWriteBytes,一个是doWriteFileRegion,这里先介绍doWriteBytes,后面有时间再介绍doWriteFileRegion。好了,我们继续看doWriteBytes这个函数的实现。它被定义在NioSocketChannel中,这是我们用来处理客户端连接常用的Channel。

    @Override
    protected int doWriteBytes(ByteBuf buf) throws Exception {
        final int expectedWrittenBytes = buf.readableBytes();
        final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);
        return writtenBytes;
    }
上面的代码最终调用了buf.readBytes(channel, bytes)函数,其实这里已经开始调用真正的JDK的Socket Channel开始发送数据了。我们还是来简单的看看这个函数,后面有时间再详细介绍ByteBuf整个结构。直接上代码,这两个函数分别定义在AbstractByteBuf类和ReadOnlyByteBufferBuf中,可以看出最终就是通过JDK的channel.write函数进行数据发送。

    @Override
    public int readBytes(GatheringByteChannel out, int length)
            throws IOException {
        checkReadableBytes(length);
        int readBytes = getBytes(readerIndex, out, length);
        readerIndex += readBytes;
        return readBytes;
    }

    @Override
    public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {
        ensureAccessible();
        if (length == 0) {
            return 0;
        }

        ByteBuffer tmpBuf = internalNioBuffer();
        tmpBuf.clear().position(index).limit(index + length);
        return out.write(tmpBuf);
    }

3、总结

本文简单的分析了write和flush的过程,从上到下的理了一遍数据write和flush的完整过程。另外在最后发送数据的时候,我们提到了ByteBuf,这个在后面我会写一篇文章来详细介绍。




  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值