java netty之一个write跟踪(数据发送)

来源:互联网 发布:弯曲法测杨氏模量数据 编辑:程序博客网 时间:2024/06/04 18:07

在深入分析ServerBootstrap之前,先来跟踪一个write的过程吧,也就是数据发送的过程。。。。

先来看看这个例子的代码吧,server部分的初始化:

public class NettyServer {public void run() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup);   //前者用来处理accept事件,后者用于处理已经建立的连接的iob.channel(NioServerSocketChannel.class);   //用它来建立新accept的连接,用于构造serversocketchannel的工厂类b.childHandler(new ChannelInitializer<SocketChannel>(){      //为accept的pipeline预添加的inboundhandler@Override     //当新连接accept的时候,这个方法会调用protected void initChannel(SocketChannel ch) throws Exception {// TODO Auto-generated method stubch.pipeline().addLast(new MyChannelHandler());}});ChannelFuture f = b.bind(80).sync();    //在所有的网卡上监听这个端口f.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}public static void main(String args[]) throws Exception {new NettyServer().run();}}
接下来是handler部分的代码:
public class MyChannelHandler extends ChannelInboundByteHandlerAdapter {@Overrideprotected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in)throws Exception {// TODO Auto-generated method stub//while (in.isReadable()) {//System.out.print((char)in.readByte());//}ByteBuf b = ctx.alloc().buffer();b.writeBytes("aaa".getBytes());ctx.pipeline().write(b);ctx.pipeline().flush().addListener(new ChannelFutureListener(){@Overridepublic void operationComplete(ChannelFuture future)throws Exception {// TODO Auto-generated method stubfuture.channel().close().sync();}});}}
这个要实现的功能还是很简单的,当建立了连接并收到数据之后,向客户端发送aaa。。。好了,接下来我们来跟踪一下整个过程。。。。

代码首先创建了一个ByteBuf,然后再将aaa写入到buffer里面,然后再发送buf,那是因为NioSocketChannel是基于byte的,因此它的unsafe对象也是基于byte的。。。。

先看看pipeline的write方法:(defaultchannelpipeline)

    //从尾handler的context的write方法    public ChannelFuture write(Object message) {        return tail.write(message);    }
这里调用的是tailhander的context的write方法,而tailhandler是一个inboundhandler,因此会向前寻找到第一个oubounhander来处理,在前面的文章也有提到过对于读取的数据,pipeline上面的处理流程是从head到tail,而对于发送出去的数据,则是从tail到head。

好了,接下来看context的write方法:(defaultchannelhandlercontext)

    //调用write方法,并将创建的promise返回    public ChannelFuture write(Object message) {        return write(message, newPromise());    }
额,接着再来看这里调用的write方法吧:
    @Override    public ChannelFuture write(final Object message, final ChannelPromise promise) {        if (message instanceof FileRegion) {        //如果是fileregion的话,            return sendFile((FileRegion) message, promise);        }        if (message == null) {            throw new NullPointerException("message");        }        validateFuture(promise);        DefaultChannelHandlerContext ctx = prev;        EventExecutor executor;        final boolean msgBuf;//向上寻找到第一个有buf的outboundhandler        if (message instanceof ByteBuf) {        //如果是bytebuf的类型,那么向上找到第一个有outbytebuffer的outboundhandler            for (;;) {                if (ctx.hasOutboundByteBuffer()) {                                    msgBuf = false;   //表示不是msg,是byte                    executor = ctx.executor();                    break;                }//message的类型,基于byte的handler和基于message的handler都行,因为最终到了head都会处理,成为基于byte的                if (ctx.hasOutboundMessageBuffer()) {                                    msgBuf = true;  //                    executor = ctx.executor();                    break;                }                ctx = ctx.prev;            }        } else {            msgBuf = true;            for (;;) {                if (ctx.hasOutboundMessageBuffer()) {                    executor = ctx.executor();                    break;                }                ctx = ctx.prev;            }        }//调用刚刚找到的那个有outbuf的handler的context的write0方法        if (executor.inEventLoop()) {            ctx.write0(message, promise, msgBuf);            return promise;        }        final DefaultChannelHandlerContext ctx0 = ctx;        executor.execute(new Runnable() {            @Override            public void run() {                ctx0.write0(message, promise, msgBuf);            }        });        return promise;    }
这部分代码就能很明显的看出是从tail向head方向寻找的吧。。。由于我们前期并没有定义outboundhandler,所以这里找到的将是pipeline上面默认的headhandler,好了,接下来来看write0方法吧:
//将数据写入到outbuf中,并调用flush方法    private void write0(Object message, ChannelPromise promise, boolean msgBuf) {        Channel channel = channel();        //如果当前的channel并没有        if (!channel.isRegistered() && !channel.isActive()) {            promise.setFailure(new ClosedChannelException());            return;        }        if (isOutboundFreed()) {            promise.setFailure(new ChannelPipelineException(                    "Unable to write as outbound buffer of next handler was freed already"));            return;        }        if (msgBuf) {            //加入message            outboundMessageBuffer().add(message);        } else {            ByteBuf buf = (ByteBuf) message;            try {            //将要发送的数据写进outhandler                outboundByteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes());            } finally {                buf.release();            }        }        //flush操作,将缓冲区的数据发送出去        invokeFlush0(promise);    }
将数据写入到outbuf中去,然后最后调用invokeFlush0,好了这里来看看这个invokeFlush0f方法吧:
    //将buf里面的数据flush出去    private void invokeFlush0(ChannelPromise promise) {        if (isOutboundFreed()) {            promise.setFailure(new ChannelPipelineException(                    "Unable to flush as outbound buffer of next handler was freed already"));            return;        }        Channel channel = channel();   //获取channel        if (!channel.isActive() && !channel.isRegistered()) {            promise.setFailure(new ClosedChannelException());            return;        }//提取当前的context的handler,因为是outboundhandler,所以也是operationhandler        ChannelOperationHandler handler = (ChannelOperationHandler) handler();        if (handler instanceof ChannelOutboundHandler) {        //将bridge部分的数据flush,留在以后看            flushOutboundBridge();        }        try {        //真实的调用poutboundhandler或者说operationhandler的flush方法,用于将数据发送出去        //其实是headhandler的话,其实就是调用其unsafe对象的flush方法,说白了也就是所属channel的unsafe对象的flush方法            handler.flush(this, promise);        } catch (Throwable t) {            notifyHandlerException(t);        } finally {            if (handler instanceof ChannelOutboundByteHandler && !isOutboundFreed()) {                try {                    ((ChannelOutboundByteHandler) handler).discardOutboundReadBytes(this);                } catch (Throwable t) {                    notifyHandlerException(t);                }            }            freeHandlerBuffersAfterRemoval();        }    }
代码还是很简单吧,说白了就是调用当前context的handler的flush方法,我们这里由于并没有定义自己的outboundhandler,所以这地调用的pipeline默认的headhandler的flush方法来将buf中的数据真正的发送出去。。

好了,那么接下来我们来看看headhandler的flush方法,由于NioSocketChannel是基于byte的,所以我们也看基于byte的headhandler的flush方法吧:

        @Override        public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {            int discardedMessages = 0;            MessageBuf<Object> in = msgSink;            for (;;) {            //这里也是存在一定的可能性将要发送的数据写到了msgbuf中去                Object m = in.poll();                if (m == null) {                    break;                }                if (m instanceof ByteBuf) {                    ByteBuf src = (ByteBuf) m;                    //将数据提取出来,并写到bytebuf中去                    byteSink.writeBytes(src, src.readerIndex(), src.readableBytes());                } else {                    logger.debug(                            "Discarded outbound message {} that reached at the head of the pipeline. " +                                    "Please check your pipeline configuration.", m);                    discardedMessages ++;                }                BufUtil.release(m);            }            if (discardedMessages != 0) {                logger.warn(                        "Discarded {} outbound message(s) that reached at the head of the pipeline. " +                        "Please check your pipeline configuration.", discardedMessages);            }       //其实最终还是调用usafe的flush来写数据            unsafe.flush(promise);        }    }
嗯,这个还是比较简单的吧,说白了还是调用的channel的unsafe对象的flush方法来发送数据的,好了感觉这篇文章是给自己挖了一个坑啊。。我擦,还要去看unsafe对象的flush方法,好吧,那接下来来看NioSocketChannel的unsafe对象的flush方法吧,其定义在AbstractChannel里的AbstractUnsafe中
        @Override        //具体的将缓冲区的数据flush        public void flush(final ChannelPromise promise) {            if (eventLoop().inEventLoop()) {            //如果已经有需要flush的任务了,那么将flush的任务挂起就好了                FlushTask task = flushTaskInProgress;                if (task != null) {                    // loop over the tasks to find the last one                    for (;;) {                        FlushTask t = task.next;                        if (t == null) {                            break;                        }                        task = t.next;                    }                    task.next = new FlushTask(null, promise);                    return;                }                //here                //调用这个方法                flushNotifierAndFlush(promise);            } else {                eventLoop().execute(new Runnable() {                    @Override                    public void run() {                        flush(promise);                    }                });            }        }
代码还是很简单,好了接下来继续来看flushNotifierAndFlush方法的定义吧:
        private void flushNotifierAndFlush(ChannelPromise promise) {            flushNotifier(promise);            //调用flush0方法来将数据发送数据            flush0();        }
好吧,直接来看flush0方法吧:
        private void flush0() {            if (!inFlushNow) { // Avoid re-entrance                try {                    // Flush immediately only when there's no pending flush.                    // If there's a pending flush operation, event loop will call flushNow() later,                    // and thus there's no need to call it now.                    if (!isFlushPending()) {                        //调用flushnow方法将数据flush出去                    flushNow();                    }                } catch (Throwable t) {                    flushFutureNotifier.notifyFlushFutures(t);                    if (t instanceof IOException) {                        close(voidFuture());                    }                }            } else {            //执行flush的任务队列                if (!flushNowPending) {                    flushNowPending = true;                    eventLoop().execute(flushLaterTask);                }            }        }
这里分为两种情况吧,不过一般都是调用flushNowf方法将数据发送出去,不过当数据发送很慢的时候,还是可能将刮起的flush任务执行的,好了接下来来看flushNow方法吧:
        @Override        public final void flushNow() {            if (inFlushNow || flushTaskInProgress != null) {                return;            }            inFlushNow = true;            //here            //获取headhandler的context,也就是pipeline里面自定义的那个,是outboundhandler,从他那里来获取outbuf,也就是要发送的数据            ChannelHandlerContext ctx = headContext();            Throwable cause = null;            try {                if (metadata().bufferType() == BufType.BYTE) {                    ByteBuf out = ctx.outboundByteBuffer();                    int oldSize = out.readableBytes();  //本身需要发送的数据量                    try {                    //这个方法延后到了子类中显示                        doFlushByteBuffer(out);                    } catch (Throwable t) {                        cause = t;                    } finally {                        int delta = oldSize - out.readableBytes();  //这里就相当于是已经发送了的数据量                        out.discardSomeReadBytes();                        flushFutureNotifier.increaseWriteCounter(delta);                    }                } else {                    MessageBuf<Object> out = ctx.outboundMessageBuffer();                    int oldSize = out.size();                    try {                        doFlushMessageBuffer(out);                    } catch (Throwable t) {                        cause = t;                    } finally {                        flushFutureNotifier.increaseWriteCounter(oldSize - out.size());                    }                }                if (cause == null) {                    flushFutureNotifier.notifyFlushFutures();                } else {                    flushFutureNotifier.notifyFlushFutures(cause);                    if (cause instanceof IOException) {                        close(voidFuture());                    }                }            } finally {                inFlushNow = false;            }        }
这里比较需要注意的是,是获取整个pipeline的headhandler的context,将它的buf里面的数据发送出去,其实最终调用的还是doFlushByteBuffer方法来发送数据:AbstractNioByteChannel
 //用于向channel中写数据    protected void doFlushByteBuffer(ByteBuf buf) throws Exception {        for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) {        //其实是调用doWriteBytes来写数据,这个方法延后到了后来的类中            int localFlushedAmount = doWriteBytes(buf, i == 0);            if (localFlushedAmount > 0) {                break;            }            if (!buf.isReadable()) {                // Reset reader/writerIndex to 0 if the buffer is empty.                buf.clear();                break;            }        }    }
好了,还是来看doWriteBytes方法吧,它定义在NioSocketChannel中:
//写数据到channel里面,将buf里面的数据发送出去    protected int doWriteBytes(ByteBuf buf, boolean lastSpin) throws Exception {        final int expectedWrittenBytes = buf.readableBytes();  //相当于是需要发送的数据        final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);  //这里就是真正的发送数据        final SelectionKey key = selectionKey();   //获取selectionkey        final int interestOps = key.interestOps();   //获取当前channel挂起的事件        if (writtenBytes >= expectedWrittenBytes) {        //如果想要发送的数据都已经发送完了,那么可以更新感兴趣的事件了,将write事件去除            // Wrote the outbound buffer completely - clear OP_WRITE.            if ((interestOps & SelectionKey.OP_WRITE) != 0) {                key.interestOps(interestOps & ~SelectionKey.OP_WRITE);            }        } else {            // Wrote something or nothing.            // a) If wrote something, the caller will not retry.            //    - Set OP_WRITE so that the event loop calls flushForcibly() later.            // b) If wrote nothing:            //    1) If 'lastSpin' is false, the caller will call this method again real soon.            //       - Do not update OP_WRITE.            //    2) If 'lastSpin' is true, the caller will not retry.            //       - Set OP_WRITE so that the event loop calls flushForcibly() later.        //如果没有发送完数据,那么需要挂起写事件            if (writtenBytes > 0 || lastSpin) {                if ((interestOps & SelectionKey.OP_WRITE) == 0) {                    key.interestOps(interestOps | SelectionKey.OP_WRITE);                }            }        }        return writtenBytes;    }

好了,上面的代码还是很简单额,基本一看就能看明白,那么这个发送的流程也就走完了。。。

够长的。。感觉写的也不够仔细。。就这样吧。。

接下来可以看serverbootstrap的流程了。。。。