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、批量写数据是,如果尝试写的都写入了,下次会尝试更多。


杜若
67 声望3 粉丝