一般的开发者write和flush数据,都是基于ChannelHandlerContext ctx,然后调用其相应的write和flush方法。下面分别对这两个方法进行代码分析。(这里顺便提示一下,这两个方法都是从应用往底层发数据,属于OutboundHandler类型。如果没有特别的需求,我们不需要定义自己的handler,可以使用默认的Handler,这个在后面的分析中会体现。)
下面是ChannelHandlerContext的具体类DefaultChannelHandlerContext中write的相关代码
@Override public ChannelFuture write(Object msg) { return write(msg, newPromise()); //构造了一个newPromise对象,调用带promise的write方法, } @Override public ChannelFuture write(final Object msg, final ChannelPromise promise) { if (msg == null) { throw new NullPointerException("msg"); } validatePromise(promise, true);//验证promise write(msg, false, promise);//再一次调用write方法,flush参数设置为false,这个参数比较重要,真正的数据发送过程是flush,后面会提到 return promise; } private void write(Object msg, boolean flush, ChannelPromise promise) { DefaultChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeWrite(msg, promise);//调用invokeWrite函数 if (flush) { next.invokeFlush(); } } else { int size = channel.estimatorHandle().size(msg); if (size > 0) { ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer(); // Check for null as it may be set to null if the channel is closed already if (buffer != null) { buffer.incrementPendingOutboundBytes(size, false); } } executor.execute(WriteTask.newInstance(next, msg, size, flush, promise)); } } <pre name="code" class="java"> private void invokeWrite(Object msg, ChannelPromise promise) { try { ((ChannelOutboundHandler) handler).write(this, msg, promise);//调用handler的write方法,这里我们使用HeadHandler默认的Handler方法。 } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } }下面再看HeadHandler类的write方法
@Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { unsafe.write(msg, promise); }还记得之前,我们提到的unsafe对象吗,从这个代码中,我们可以看出unsafe对象的重要性了,真正的“苦力”还是unsafe对象啊,所以不容忽视它,赶紧来看看它的实现,下面是unsafe的write方法,主要unsafe是一个接口,下面又多个扩展的具体类,这里的write方法在protected abstract class AbstractUnsafe中定义,注意它是AbstractChannel的一个内部类。下面就是write的定义。
@Override public void write(Object msg, ChannelPromise promise) { if (!isActive()) { // Mark the write request as failure if the channel is inactive. if (isOpen()) { promise.tryFailure(NOT_YET_CONNECTED_EXCEPTION); } else { promise.tryFailure(CLOSED_CHANNEL_EXCEPTION); } // release message now to prevent resource-leak ReferenceCountUtil.release(msg); } else { outboundBuffer.addMessage(msg, promise); } }通过这个代码,我们可以清楚的看到,其实write的过程,只是把数据写入了一个buffer中,并没有真正的发送。真正的发送过程是我们后面要分析的flush的过程,好吧赶紧看看flush的过程。
上面讲到flush是真正的发送数据的过程,所以这个方法还是比较关键的。先来看看DefaultChannelHandlerContext中的相关代码:
@Override public ChannelHandlerContext flush() { final DefaultChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeFlush(); } else { Runnable task = next.invokeFlushTask; if (task == null) { next.invokeFlushTask = task = new Runnable() { @Override public void run() { next.invokeFlush(); } }; } executor.execute(task); } return this; } private void invokeFlush() { try { ((ChannelOutboundHandler) handler).flush(this); } catch (Throwable t) { notifyHandlerException(t); } }这个代码逻辑还是比较清晰的,还是我们之前分析的最终调用默认的HeadHandler来处理flush,下面是HeadHandler的flush定义。
@Override public void flush(ChannelHandlerContext ctx) throws Exception { unsafe.flush(); }这里又是unsafe这个苦力,在做事情。那就看看unsafe中flush的定义吧,仍然是在protected abstract class AbstractUnsafe中定义的,注意它是AbstractChannel的一个内部类。
@Override public void flush() { ChannelOutboundBuffer outboundBuffer = this.outboundBuffer; if (outboundBuffer == null) { return; } outboundBuffer.addFlush(); flush0(); } 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(NOT_YET_CONNECTED_EXCEPTION); } else { outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION); } } finally { inFlush0 = false; } return; } try { doWrite(outboundBuffer); } catch (Throwable t) { outboundBuffer.failFlushed(t); if (t instanceof IOException) { close(voidPromise()); } } finally { inFlush0 = false; } }逻辑仍然很简单,我们看到这里它最终是调用了doWrite函数,而且以buffer作参数,这个buffer其实就是write时数据存储的地方。write的过程这是就要和Channel打交道了,还记得之前我们分析过的channel吗,这里write定义在AbstractNioByteChannel类中(Server端以字节的形式发送数据),下面是具体的代码:
@Override protected void doWrite(ChannelOutboundBuffer in) throws Exception { int writeSpinCount = -1; for (;;) { Object msg = in.current(); if (msg == null) { // Wrote all messages. clearOpWrite(); break; } if (msg instanceof ByteBuf) { ByteBuf buf = (ByteBuf) msg; int readableBytes = buf.readableBytes(); if (readableBytes == 0) { in.remove(); continue; } if (!buf.isDirect()) { ByteBufAllocator alloc = alloc(); if (alloc.isDirectBufferPooled()) { // Non-direct buffers are copied into JDK's own internal direct buffer on every I/O. // We can do a better job by using our pooled allocator. If the current allocator does not // pool a direct buffer, we rely on JDK's direct buffer pool. buf = alloc.directBuffer(readableBytes).writeBytes(buf); in.current(buf); } } boolean done = false; long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i --) { int localFlushedAmount = doWriteBytes(buf); if (localFlushedAmount == 0) { break; } flushedAmount += localFlushedAmount; if (!buf.isReadable()) { done = true; break; } } in.progress(flushedAmount); if (done) { in.remove(); } else { // Did not write completely. setOpWrite(); break; } } else if (msg instanceof FileRegion) { FileRegion region = (FileRegion) msg; boolean done = false; long flushedAmount = 0; if (writeSpinCount == -1) { writeSpinCount = config().getWriteSpinCount(); } for (int i = writeSpinCount - 1; i >= 0; i --) { long localFlushedAmount = doWriteFileRegion(region); if (localFlushedAmount == 0) { break; } flushedAmount += localFlushedAmount; if (region.transfered() >= region.count()) { done = true; break; } } in.progress(flushedAmount); if (done) { in.remove(); } else { // Did not write completely. setOpWrite(); break; } } else { throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg)); } } }从上面的代码逻辑,我们看到了最终write可能调用两个函数,一个是doWriteBytes,一个是doWriteFileRegion,这里先介绍doWriteBytes,后面有时间再介绍doWriteFileRegion。好了,我们继续看doWriteBytes这个函数的实现。它被定义在NioSocketChannel中,这是我们用来处理客户端连接常用的Channel。
@Override protected int doWriteBytes(ByteBuf buf) throws Exception { final int expectedWrittenBytes = buf.readableBytes(); final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes); return writtenBytes; }上面的代码最终调用了buf.readBytes(channel, bytes)函数,其实这里已经开始调用真正的JDK的Socket Channel开始发送数据了。我们还是来简单的看看这个函数,后面有时间再详细介绍ByteBuf整个结构。直接上代码,这两个函数分别定义在AbstractByteBuf类和ReadOnlyByteBufferBuf中,可以看出最终就是通过JDK的channel.write函数进行数据发送。
@Override public int readBytes(GatheringByteChannel out, int length) throws IOException { checkReadableBytes(length); int readBytes = getBytes(readerIndex, out, length); readerIndex += readBytes; return readBytes; } @Override public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { ensureAccessible(); if (length == 0) { return 0; } ByteBuffer tmpBuf = internalNioBuffer(); tmpBuf.clear().position(index).limit(index + length); return out.write(tmpBuf); }
本文简单的分析了write和flush的过程,从上到下的理了一遍数据write和flush的完整过程。另外在最后发送数据的时候,我们提到了ByteBuf,这个在后面我会写一篇文章来详细介绍。