Netty基本介绍,参考 Netty与网络编程
1、源码分析,EchoServerHandler之Write流程
1.1 write流程入口
通常我们通过ChannelRead收到消息后,需要给一个响应,通过ctx.write()将响应返回客户端。
在自定义handler的channelRead方法打一个断点,客户端发起请求,并进入ctx.write
1.2 AbstractChannelHandlerContext#write方法
我们看到下图中,先通过findContextOutbound方法找到下一个责任节点再执行。
如果我们自定义方法里用的是ctx.writeAndFlush(写入并发送数据),那么下面就是走第一个分支,否则走第二个分支,继续进入next.invokeWrite
1.3 invokeWrite写入数据方法
进入了责任链的头节点HeadContext的invokeWrite方法,接着进入invokeWrite0
1.4 invokeWrite0执行handler写入方法
这里根据handler的类型调用不同的实现类的方法,我们这里是headContext.write方法,继续进入
1.5 AbstractChannel.AbstractUnsafe#write方法
我们来到AbstractChannel.AbstractUnsafe#write,又是Unsafe类型,Netty的大量读写操作在这个类,进入最后一行的方法addMessage,该方法里面有对写入高水位的判断,
然后再addMessage方法中进入incrementPendingOutboundBytes
1.6 ChannelOutboundBuffer#incrementPendingOutboundBytes高水位判断
下图,判断待发送的size是不是高于高水位(默认高水位是60M),如果是,那么unwritable设置为false。应用可以根据这个标志决定是否继续发送数据。
通过CAS操作,设置buffer的unwritable属性:
1.7 应用层如何处理unwritable标志
如图,应用层可以通过上下文ctx获取可以标志,这个标志最终是从ChannelOutboundBuffer里获取的
2、Flush流程
2.1 EchoServerHandler.channelReadComplete
在自定义handler的channelReadComplete方法打一个断点,客户端发起请求,并进入ctx.flush,一直往下走,我们来到DefaultChannelPipeline.HeadContext#flush方法
2.2 进入AbstractChannel.AbstractUnsafe#flush0方法
又来到的unsafe的Flush方法,进入doWrite
2.3 NioSocketChannel#doWrite写操作执行16次
写数据在do-while循环中执行,默认执行16次,writeSpinCount初始是16。如果16次没有完成,截止schedule一个新的flush task出来,而不是注册写事件
2.4 NioSocketChannel#doWrite的while循环
看一下doWrite的while循环
- 最多返回1024个buffer,总数量尽量不超过maxBytesPerGatheringWrite
- nioBufferCnt=1 ,单个bytebuffer
- nioBufferCnt>1 ,批量数据多个bytebuffer。如果缓冲区写满了,注册写事件,启用更多线程来处理
3.总结
写数据首先通过Write将数据写入Buffer(ChannelOutboundBuffer),然后通过Flush将数据发送出去。写数据包含两个流程Write和flush。
1、Netty写数据太多时,超过一定水位线,会将unwritable标志置为false,应用端根据这个标志决定要不要发送数据
2、只要有数据,就一直写,写到16次,如果还没有写完,会重启一个线程继续写
3、批量写数据是,如果尝试写的都写入了,下次会尝试更多。