在深入分析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(){ //为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
嗯,这个还是比较简单的吧,说白了还是调用的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
这里比较需要注意的是,是获取整个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的流程了。。。。