netty EventLoop write() 源码分析(二)

netty 高并发物联网交流群 651219170

Netty 的write() 和 flush()

下面我们就跟踪下 write() 和 flush() .验证两个事情。
1.在非 EventLoop 线程 write() 是线程安全的。
2.write() 只是把数据放到了 ChannelOutboundBuffer 中。flush() 才是把数据发送出去(在 EventLoop 线程调用的话,可以直接发送出去。否则把 flushedEntry 指向为非 null,并注册可写事件,等待 EventLoop 线程发送出去)。

ctx.write(“recvied message”) 开始向下跟踪最终发现不管是 write() 还是 writeAndFlush() 都是调用了AbstractChannelHandlerContext 的 write() 方法。
write() 就是获取下一个 Outbuondhandler 然后调用 write()

 private void write(Object msg, boolean flush, ChannelPromise promise) {
        //找到注册的(addFirst)下一个 ChannelOutBound 的 context
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();

        //如果当前 write() 是 eventLoop 线程则执行 write() 操作,否则添加一个写任务。
        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 线程调用 write() 和 flush()

调用的 handler 的 write()

在上节的代码可以看出来我们在 EventLoop 线程调用 write() 和非 EventLoop 调用 write() 是有一个分支的(和明显,在 EventLoop 线程的话不需要考虑竞争问题)。

    private void invokeWrite(Object msg, ChannelPromise promise) {
        //检查下 handlerAdded 是否调用完毕(包括其他的一些状态)
        if (invokeHandler()) {
            //调用这个 handler 的 wirte() 这个 handler 就是 ChannelOutboundHandler
            invokeWrite0(msg, promise);
        } else {
            //否则就直接调用下个 outboundhandler 的 write()
            write(msg, promise);
        }
    }

    private void invokeWrite0(Object msg, ChannelPromise promise) {
        try {
            //最后调用的是 HeadContext 的 wrire()
            ((ChannelOutboundHandler) handler()).write(this, msg, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }

HeadContext 的 write()
调用当前这个 handdler 的 write() 。我们自定义的 handdler 一般都会调用 supper.write() 来调用下个 handdler 的 write()。当链表到达头部的时候就会调用 netty 定义的 HeadContext 的 write()(因为是 write() 是出站,所以最后调用的是 HeadContext)
出站流程
此图借用 netty in action
所以查看这HeadContext.write()->AbstractUnsafe.write()
在这个方法里可以看到。msg 被加到了 ChannelOutboundBuffer 里面。

Netty 线程调用 flush()

下面我们来看下。flush() 做了什么。
和 write() 一样的出站。会先调用我们的 write() 最后调用 HeadContext 的 flush().
HeadContext.flush()->AbstractUnsafe.flush()

  public final void flush() {
            assertEventLoop();

            ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
            if (outboundBuffer == null) {
                return;
            }
            //把 flushedEntry 只向了第一个未发送的 Entry
            outboundBuffer.addFlush();
            //在这里调用了 doWrite()
            flush0();
        }

addFlush() 之前我们看过,让 flushedEntry 指向了第一个为发送的数据。做好发送准备。
flush0() 里面调用了 doWrite() 这个方法在前面有详细的介绍了。就是这个方法通过 channle 发送了数据。
我们这里讲的都是调用write() 和 flush() 在 EventLoop 线程所以直接发送是没有线程竞争的(也就是说不需要先在 Selector 注册个事件然后在发通过 EventLoop 线程发送出去)。

不在 EventLoop 线程调用 write()

不在 EventLoop 线程的封装成两个 task 然后调用 safeExecute() 执行就好了。safeExecute()的第一个参数就是 EventLoop 线程。所以说不在 EventLoop 线程调用 write() 或者 flush() 其实都是封装成了一个任务然后调用了等待 EventLoop 线程调用。还记得之前我们在看 EventLoop 代码的时候有个 ioRate 觉定了除了执行 IO 事件,还要执行 runAllTask() 。

          //获取 EventLoop 线程
          EventExecutor executor = next.executor();
          AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);

WriteTask

先看看类图。
WriteTask的类图
既然是在线程里执行那肯定得实现run() 方法。
这个 run 就在 AbstractWriteTask 里。
run()->write()
wrire() 被子类 WriteTask 重新了。

protected void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
    ctx.invokeWrite(msg, promise);
}

其中 ctx.write()->调用了 AbstractChannelContext.write()之前已经加过了。最后就是把任务加到了 outboundBuffer 这个链表。

 private void invokeWrite(Object msg, ChannelPromise promise) {
        //检查下 handlerAdded 是否调用完毕(包括其他的一些状态)
        if (invokeHandler()) {
            //调用这个 handler 的 wirte()
            invokeWrite0(msg, promise);
        } else {
            //否则就递归调用处理下个 handler 的 write()
            write(msg, promise);
        }
    }

WriteAndFlushTask

WriteAndFlushTask 和 WriteTask 原理一样。只是重写了 write()

  public void write(AbstractChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
            super.write(ctx, msg, promise);
            ctx.invokeFlush();
        }

在调用 write() 的基础上多了一步调用 ctx.flush() 的操作。
ctx.flush() 为什么能够真正把数据发送出去在上面说过了。这里不再重复。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章