一般的开发者write和flush数据,都是基于ChannelHandlerContext ctx,然后调用其相应的write和flush方法。下面分别对这两个方法进行代码分析。(这里顺便提示一下,这两个方法都是从应用往底层发数据,属于OutboundHandler类型。如果没有特别的需求,我们不需要定义自己的handler,可以使用默认的Handler,这个在后面的分析中会体现。)

1、write方法

下面是ChannelHandlerContext的具体类DefaultChannelHandlerContext中write的相关代码



1. @Override  
2. public ChannelFuture write(Object msg) {  
3. return write(msg, newPromise()); //构造了一个newPromise对象,调用带promise的write方法,  
4. }  
5.   
6. @Override  
7. public ChannelFuture write(final Object msg, final ChannelPromise promise) {  
8. if (msg == null) {  
9. throw new NullPointerException("msg");  
10.     }  
11.   
12. true);//验证promise  
13.   
14. false, promise);//再一次调用write方法,flush参数设置为false,这个参数比较重要,真正的数据发送过程是flush,后面会提到  
15.   
16. return promise;  
17. }  
18.   
19. private void write(Object msg, boolean flush, ChannelPromise promise) {  
20.   
21.     DefaultChannelHandlerContext next = findContextOutbound();  
22.     EventExecutor executor = next.executor();  
23. if (executor.inEventLoop()) {  
24. //调用invokeWrite函数  
25. if (flush) {  
26.             next.invokeFlush();  
27.         }  
28. else {  
29. int size = channel.estimatorHandle().size(msg);  
30. if (size > 0) {  
31.             ChannelOutboundBuffer buffer = channel.unsafe().outboundBuffer();  
32. // Check for null as it may be set to null if the channel is closed already  
33. if (buffer != null) {  
34. false);  
35.             }  
36.         }  
37.         executor.execute(WriteTask.newInstance(next, msg, size, flush, promise));  
38.     }  
39. }  
40.   
41. pre name="code" class="java">    private void invokeWrite(Object msg, ChannelPromise promise) {  
42. try {  
43. this, msg, promise);//调用handler的write方法,这里我们使用HeadHandler默认的Handler方法。  
44. catch (Throwable t) {  
45.         notifyOutboundHandlerException(t, promise);  
46.     }  
47. }

下面再看HeadHandler类的write方法


1. @Override  
2. public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {  
3.     unsafe.write(msg, promise);  
4. }


还记得之前,我们提到的unsafe对象吗,从这个代码中,我们可以看出unsafe对象的重要性了,真正的“苦力”还是unsafe对象啊,所以不容忽视它,赶紧来看看它的实现,下面是unsafe的write方法,主要unsafe是一个接口,下面又多个扩展的具体类,这里的write方法在protected abstract class AbstractUnsafe中定义,注意它是AbstractChannel的一个内部类。下面就是write的定义。



1. @Override  
2. public void write(Object msg, ChannelPromise promise) {  
3. if (!isActive()) {  
4. // Mark the write request as failure if the channel is inactive.  
5. if (isOpen()) {  
6.             promise.tryFailure(NOT_YET_CONNECTED_EXCEPTION);  
7. else {  
8.             promise.tryFailure(CLOSED_CHANNEL_EXCEPTION);  
9.         }  
10. // release message now to prevent resource-leak  
11.         ReferenceCountUtil.release(msg);  
12. else {  
13.         outboundBuffer.addMessage(msg, promise);  
14.     }  
15. }

通过这个代码,我们可以清楚的看到,其实write的过程,只是把数据写入了一个buffer中,并没有真正的发送。真正的发送过程是我们后面要分析的flush的过程,好吧赶紧看看flush的过程。


2、flush方法

上面讲到flush是真正的发送数据的过程,所以这个方法还是比较关键的。先来看看DefaultChannelHandlerContext中的相关代码:



1. @Override  
2. public ChannelHandlerContext flush() {  
3. final DefaultChannelHandlerContext next = findContextOutbound();  
4.     EventExecutor executor = next.executor();  
5. if (executor.inEventLoop()) {  
6.         next.invokeFlush();  
7. else {  
8.         Runnable task = next.invokeFlushTask;  
9. if (task == null) {  
10. new Runnable() {  
11. @Override  
12. public void run() {  
13.                     next.invokeFlush();  
14.                 }  
15.             };  
16.         }  
17.         executor.execute(task);  
18.     }  
19.   
20. return this;  
21. }  
22.   
23. private void invokeFlush() {  
24. try {  
25. this);  
26. catch (Throwable t) {  
27.         notifyHandlerException(t);  
28.     }  
29. }


这个代码逻辑还是比较清晰的,还是我们之前分析的最终调用默认的HeadHandler来处理flush,下面是HeadHandler的flush定义。


1. @Override  
2. public void flush(ChannelHandlerContext ctx) throws Exception {  
3.     unsafe.flush();  
4. }

这里又是unsafe这个苦力,在做事情。那就看看unsafe中flush的定义吧,仍然是在protected abstract class AbstractUnsafe中定义的,注意它是AbstractChannel的一个内部类。



1. @Override  
2. public void flush() {  
3. this.outboundBuffer;  
4. if (outboundBuffer == null) {  
5. return;  
6.     }  
7.   
8.     outboundBuffer.addFlush();  
9.     flush0();  
10. }  
11.   
12. protected void flush0() {  
13. if (inFlush0) {  
14. // Avoid re-entrance  
15. return;  
16.     }  
17.   
18. final ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;  
19. if (outboundBuffer == null || outboundBuffer.isEmpty()) {  
20. return;  
21.     }  
22.   
23. true;  
24.   
25. // Mark all pending write requests as failure if the channel is inactive.  
26. if (!isActive()) {  
27. try {  
28. if (isOpen()) {  
29.                 outboundBuffer.failFlushed(NOT_YET_CONNECTED_EXCEPTION);  
30. else {  
31.                 outboundBuffer.failFlushed(CLOSED_CHANNEL_EXCEPTION);  
32.             }  
33. finally {  
34. false;  
35.         }  
36. return;  
37.     }  
38.   
39. try {  
40.         doWrite(outboundBuffer);  
41. catch (Throwable t) {  
42.         outboundBuffer.failFlushed(t);  
43. if (t instanceof IOException) {  
44.             close(voidPromise());  
45.         }  
46. finally {  
47. false;  
48.     }  
49. }

逻辑仍然很简单,我们看到这里它最终是调用了doWrite函数,而且以buffer作参数,这个buffer其实就是write时数据存储的地方。write的过程这是就要和Channel打交道了,还记得之前我们分析过的channel吗,这里write定义在AbstractNioByteChannel类中(Server端以字节的形式发送数据),下面是具体的代码:


1. @Override  
2. protected void doWrite(ChannelOutboundBuffer in) throws Exception {  
3. int writeSpinCount = -1;  
4.   
5. for (;;) {  
6.         Object msg = in.current();  
7. if (msg == null) {  
8. // Wrote all messages.  
9.             clearOpWrite();  
10. break;  
11.         }  
12.   
13. if (msg instanceof ByteBuf) {  
14.             ByteBuf buf = (ByteBuf) msg;  
15. int readableBytes = buf.readableBytes();  
16. if (readableBytes == 0) {  
17.                 in.remove();  
18. continue;  
19.             }  
20. if (!buf.isDirect()) {  
21.                 ByteBufAllocator alloc = alloc();  
22. if (alloc.isDirectBufferPooled()) {  
23. // Non-direct buffers are copied into JDK's own internal direct buffer on every I/O.  
24. // We can do a better job by using our pooled allocator. If the current allocator does not  
25. // pool a direct buffer, we rely on JDK's direct buffer pool.  
26.                     buf = alloc.directBuffer(readableBytes).writeBytes(buf);  
27.                     in.current(buf);  
28.                 }  
29.             }  
30. boolean done = false;  
31. long flushedAmount = 0;  
32. if (writeSpinCount == -1) {  
33.                 writeSpinCount = config().getWriteSpinCount();  
34.             }  
35. for (int i = writeSpinCount - 1; i >= 0; i --) {  
36. int localFlushedAmount = doWriteBytes(buf);  
37. if (localFlushedAmount == 0) {  
38. break;  
39.                 }  
40.   
41.                 flushedAmount += localFlushedAmount;  
42. if (!buf.isReadable()) {  
43. true;  
44. break;  
45.                 }  
46.             }  
47.   
48.             in.progress(flushedAmount);  
49.   
50. if (done) {  
51.                 in.remove();  
52. else {  
53. // Did not write completely.  
54.                 setOpWrite();  
55. break;  
56.             }  
57. else if (msg instanceof FileRegion) {  
58.             FileRegion region = (FileRegion) msg;  
59. boolean done = false;  
60. long flushedAmount = 0;  
61. if (writeSpinCount == -1) {  
62.                 writeSpinCount = config().getWriteSpinCount();  
63.             }  
64. for (int i = writeSpinCount - 1; i >= 0; i --) {  
65. long localFlushedAmount = doWriteFileRegion(region);  
66. if (localFlushedAmount == 0) {  
67. break;  
68.                 }  
69.   
70.                 flushedAmount += localFlushedAmount;  
71. if (region.transfered() >= region.count()) {  
72. true;  
73. break;  
74.                 }  
75.             }  
76.   
77.             in.progress(flushedAmount);  
78.   
79. if (done) {  
80.                 in.remove();  
81. else {  
82. // Did not write completely.  
83.                 setOpWrite();  
84. break;  
85.             }  
86. else {  
87. throw new UnsupportedOperationException("unsupported message type: " + StringUtil.simpleClassName(msg));  
88.         }  
89.     }  
90. }

从上面的代码逻辑,我们看到了最终write可能调用两个函数,一个是doWriteBytes,一个是doWriteFileRegion,这里先介绍doWriteBytes,后面有时间再介绍doWriteFileRegion。好了,我们继续看doWriteBytes这个函数的实现。它被定义在NioSocketChannel中,这是我们用来处理客户端连接常用的Channel。


1. @Override  
2. protected int doWriteBytes(ByteBuf buf) throws Exception {  
3. final int expectedWrittenBytes = buf.readableBytes();  
4. final int writtenBytes = buf.readBytes(javaChannel(), expectedWrittenBytes);  
5. return writtenBytes;  
6. }

上面的代码最终调用了buf.readBytes(channel, bytes)函数,其实这里已经开始调用真正的JDK的Socket Channel开始发送数据了。我们还是来简单的看看这个函数,后面有时间再详细介绍ByteBuf整个结构。直接上代码,这两个函数分别定义在AbstractByteBuf类和ReadOnlyByteBufferBuf中,可以看出最终就是通过JDK的channel.write函数进行数据发送。


1.  @Override  
2. public int readBytes(GatheringByteChannel out, int length)  
3. throws IOException {  
4.      checkReadableBytes(length);  
5. int readBytes = getBytes(readerIndex, out, length);  
6.      readerIndex += readBytes;  
7. return readBytes;  
8.  }  
9.   
10. @Override  
11. public int getBytes(int index, GatheringByteChannel out, int length) throws IOException {  
12.      ensureAccessible();  
13. if (length == 0) {  
14. return 0;  
15.      }  
16.   
17.      ByteBuffer tmpBuf = internalNioBuffer();  
18.      tmpBuf.clear().position(index).limit(index + length);  
19. return out.write(tmpBuf);  
20.  }


3、总结

本文简单的分析了write和flush的过程,从上到下的理了一遍数据write和flush的完整过程。另外在最后发送数据的时候,我们提到了ByteBuf,这个在后面我会写一篇文章来详细介绍。