Netty Write



ChannelOutboundInvoker 接口中定义了写出数据的方法。 每一个方法都返回一个Future引用。


1、write 方法。 直接把数据写入的OutBuffer中

ChannelFuture write(Object msg)
Request to write a message via this  ChannelHandlerContext through the  ChannelPipeline. This method will not request to actual flush, so be sure to call  flush() once you want to request to flush all pending data to the actual transport.

ChannelFuture write(Object msg,
                    ChannelPromise promise)
Request to write a message via this  ChannelHandlerContext through the  ChannelPipeline. This method will not request to actual flush, so be sure to call  flush() once you want to request to flush all pending data to the actual transport.


2、writeAndFlush() 方法.把数据写入到OutBuffer 中,并直接写入到操作系统缓冲区中。

ChannelFuture writeAndFlush(Object msg)
Shortcut for call  write(Object) and  flush().


ChannelFuture writeAndFlush(Object msg,
                            ChannelPromise promise)
Shortcut for call  write(Object, ChannelPromise) and  flush().


ChannelHandlerContext  把数据写出流程主要包括两个主要部分: 1、调用write 以及writeAndFlush()的线程。2、write 后,是否flush数据到系统缓冲区。

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        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);
        }
    }


1 、 当调用ChannelHandlerContext.write(msg)的线程与SocketChannel 关联的EventLoop是同一个线程,那么write方法会直接把数据写入到OutBuffer缓冲区中。


 buffer 缓存在ChannelOutboundBuffer 类中。

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



    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            if (flush) {
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {

            //如果调用ChannelHandlerContext.write()的线程与Channel关联的EventLoop的线程不是同一个线程,将会创建一个AbstractWriteTask 提交到EventLoop线程中执行。
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);
        }
    }

//如果调用ChannelHandlerContext.write()的线程与Channel关联的EventLoop的线程不是同一个线程,将会创建一个AbstractWriteTask 提交到EventLoop线程中执行。


    abstract static class AbstractWriteTask implements Runnable {

        private static final boolean ESTIMATE_TASK_SIZE_ON_SUBMIT =
                SystemPropertyUtil.getBoolean("io.netty.transport.estimateSizeOnSubmit", true);

        // Assuming a 64-bit JVM, 16 bytes object header, 3 reference fields and one int field, plus alignment
        private static final int WRITE_TASK_OVERHEAD =
                SystemPropertyUtil.getInt("io.netty.transport.writeTaskSizeOverhead", 48);

        private final Recycler.Handle<AbstractWriteTask> handle;
        private AbstractChannelHandlerContext ctx;
        private Object msg;
        private ChannelPromise promise;
        private int size;

        @SuppressWarnings("unchecked")
        private AbstractWriteTask(Recycler.Handle<? extends AbstractWriteTask> handle) {
            this.handle = (Recycler.Handle<AbstractWriteTask>) handle;
        }

        protected static void init(AbstractWriteTask task, AbstractChannelHandlerContext ctx,
                                   Object msg, ChannelPromise promise) {
            task.ctx = ctx;
            task.msg = msg;
            task.promise = promise;

            if (ESTIMATE_TASK_SIZE_ON_SUBMIT) {
                ChannelOutboundBuffer buffer = ctx.channel().unsafe().outboundBuffer();

                // Check for null as it may be set to null if the channel is closed already
                if (buffer != null) {
                    task.size = ctx.pipeline.estimatorHandle().size(msg) + WRITE_TASK_OVERHEAD;
                    buffer.incrementPendingOutboundBytes(task.size);
                } else {
                    task.size = 0;
                }
            } else {
                task.size = 0;
            }
        }

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

        protected void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
            ctx.invokeWrite(msg, promise);
        }
    }
并把AbstractWriteTask 任务提交到EventLoop线程中,异步写入到OutBuffer中。


在写入到OutBuffer中的缓存数据,在EventLoop.run()的过程中,当操作系统缓存区可写时,把OutBuffer中的缓存数据写入缓冲区。

   // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
            if ((readyOps & SelectionKey.OP_WRITE) != 0) {
                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
                ch.unsafe().forceFlush();
            }


然后,调用NIO  write方法,直接把数据写入系统缓冲区。


   @SuppressWarnings("deprecation")
        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(FLUSH0_NOT_YET_CONNECTED_EXCEPTION, true);
                    } else {
                        // Do not trigger channelWritabilityChanged because the channel is closed already.
                        outboundBuffer.failFlushed(FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                    }
                } finally {
                    inFlush0 = false;
                }
                return;
            }

            try {
                doWrite(outboundBuffer);
            } catch (Throwable t) {
                if (t instanceof IOException && config().isAutoClose()) {
                    /**
                     * Just call {@link #close(ChannelPromise, Throwable, boolean)} here which will take care of
                     * failing all flushed messages and also ensure the actual close of the underlying transport
                     * will happen before the promises are notified.
                     *
                     * This is needed as otherwise {@link #isActive()} , {@link #isOpen()} and {@link #isWritable()}
                     * may still return {@code true} even if the channel should be closed as result of the exception.
                     */
                    close(voidPromise(), t, FLUSH0_CLOSED_CHANNEL_EXCEPTION, false);
                } else {
                    outboundBuffer.failFlushed(t, true);
                }
            } finally {
                inFlush0 = false;
            }
        }



当调用writeAndFlush时,数据直接写入到系统缓冲区。 当数据从OutBuffer写入到操作系统缓冲区时,ChannelPromise 监听器方法就会被调用。



    	new  Thread(new Runnable() {
			@Override
			public void run() {
				ChannelFuture promise = ctx.writeAndFlush(msg);
				promise.addListener(new GenericFutureListener<Future<? super Void>>() {

					@Override
					public void operationComplete(Future<? super Void> future) throws Exception {
					
						System.out.println(future);
						
					}
				});
			}
		}).start();



调用堆栈如下:


EchoServerHandler$1$1.operationComplete(Future<Void>) line: 44
DefaultPromise<V>.notifyListener0(Future, GenericFutureListener) line: 507
DefaultChannelPromise(DefaultPromise<V>).notifyListenersNow() line: 481
DefaultChannelPromise(DefaultPromise<V>).notifyListeners() line: 420
DefaultChannelPromise(DefaultPromise<V>).trySuccess(V) line: 104
PromiseNotificationUtil.trySuccess(Promise<? super V>, V, InternalLogger) line: 48
ChannelOutboundBuffer.safeSuccess(ChannelPromise) line: 669
ChannelOutboundBuffer.remove() line: 257
ChannelOutboundBuffer.removeBytes(long) line: 337
NioSocketChannel.doWrite(ChannelOutboundBuffer) line: 448
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush0() line: 856
NioSocketChannel$NioSocketChannelUnsafe(AbstractNioChannel$AbstractNioUnsafe).flush0() line: 362
NioSocketChannel$NioSocketChannelUnsafe(AbstractChannel$AbstractUnsafe).flush() line: 823
DefaultChannelPipeline$HeadContext.flush(ChannelHandlerContext) line: 1296
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush0() line: 776
DefaultChannelPipeline$HeadContext(AbstractChannelHandlerContext).invokeFlush() line: 768
AbstractChannelHandlerContext.access$1500(AbstractChannelHandlerContext) line: 38
AbstractChannelHandlerContext$WriteAndFlushTask.write(AbstractChannelHandlerContext, Object, ChannelPromise) line: 1137
AbstractChannelHandlerContext$WriteAndFlushTask(AbstractChannelHandlerContext$AbstractWriteTask).run() line: 1078
AbstractEventExecutor.safeExecute(Runnable) line: 163
NioEventLoop(SingleThreadEventExecutor).runAllTasks(long) line: 403
NioEventLoop.run() line: 462
SingleThreadEventExecutor$5.run() line: 858
DefaultThreadFactory$DefaultRunnableDecorator.run() line: 138
FastThreadLocalThread(Thread).run() line: 745


在NIO 实际写入到系统缓冲区时NioSocketChannel.doWrite(ChannelOutboundBuffer) 


   /**
     * Will remove the current message, mark its {@link ChannelPromise} as success and return {@code true}. If no
     * flushed message exists at the time this method is called it will return {@code false} to signal that no more
     * messages are ready to be handled.
     */
    public boolean remove() {
        Entry e = flushedEntry;
        if (e == null) {
            clearNioBuffers();
            return false;
        }
        Object msg = e.msg;

        ChannelPromise promise = e.promise;
        int size = e.pendingSize;

        removeEntry(e);

        if (!e.cancelled) {
            // only release message, notify and decrement if it was not canceled before.
            ReferenceCountUtil.safeRelease(msg);
            safeSuccess(promise);
            decrementPendingOutboundBytes(size, false, true);
        }

        // recycle the entry
        e.recycle();

        return true;
    }

   /**
     * Will remove the current message, mark its {@link ChannelPromise} as success and return {@code true}. If no
     * flushed message exists at the time this method is called it will return {@code false} to signal that no more
     * messages are ready to be handled.
     */
    public boolean remove() {
        Entry e = flushedEntry;
        if (e == null) {
            clearNioBuffers();
            return false;
        }
        Object msg = e.msg;

        ChannelPromise promise = e.promise;
        int size = e.pendingSize;

        removeEntry(e);

        if (!e.cancelled) {
            // only release message, notify and decrement if it was not canceled before.
            ReferenceCountUtil.safeRelease(msg);
            safeSuccess(promise);
            decrementPendingOutboundBytes(size, false, true);
        }

        // recycle the entry
        e.recycle();

        return true;
    }



Notify通过异步方式来调用。


   private void notifyListeners() {
        EventExecutor executor = executor();
        if (executor.inEventLoop()) {
            final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
            final int stackDepth = threadLocals.futureListenerStackDepth();
            if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
                threadLocals.setFutureListenerStackDepth(stackDepth + 1);
                try {
                    notifyListenersNow();
                } finally {
                    threadLocals.setFutureListenerStackDepth(stackDepth);
                }
                return;
            }
        }

        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                notifyListenersNow();
            }
        });
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值