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

在深入分析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事件,后者用于处理已经建立的连接的io
			b.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 stub
					ch.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 {

	@Override
	protected 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(){

			@Override
			public void operationComplete(ChannelFuture future)
					throws Exception {
				// TODO Auto-generated method stub
				future.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的流程了。。。。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值