深入浅出Netty:write

本系列:


上一章节中,分析了Netty如何处理read事件,本节分析Netty如何把数据写会客户端。

把数据返回客户端,需要经历三个步骤:

  • 1、申请一块缓存buf,写入数据。
  • 2、将buf保存到ChannelOutboundBuffer中。
  • 3、将ChannelOutboundBuffer中的buff输出到socketChannel中。

为什么需要把buf保存到ChannelOutboundBuffer?

ctx.write()实现:

默认情况下,findContextOutbound()会找到pipeline的head节点,触发write方法。

outboundBuffer 随着Unsafe一起实例化,最终将msg通过outboundBuffer封装起来。

ChannelOutboundBuffer内部维护了一个Entry链表,并使用Entry封装msg。

1、unflushedEntry:指向链表头部
2、tailEntry:指向链表尾部
3、totalPendingSize:保存msg的字节数
4、unwritable:不可写标识

通过Entry.newInstance返回Entry实例,Netty对Entry采用了缓存策略,使用完的Entry实例需要清空并回收,难道是因为Entry实例化比较耗时?

新的entry默认插入链表尾部,并让tailEntry指向它。

2184951-53e95abefcc0504f

方法incrementPendingOutboundBytes主要采用CAS更新totalPendingSize字段,并判断当前totalPendingSize是否超过阈值writeBufferHighWaterMark,默认是65536。如果totalPendingSize >= 65536,则采用CAS更新unwritable为1,并触发ChannelWritabilityChanged事件。

到此为止,全部的buf数据已经保存在outboundBuffer中。

ctx.flush()实现:

默认情况下,findContextOutbound()会找到pipeline的head节点,触发flush方法。

方法addFlush主要对write过程添加的msg进行flush标识,其实我不清楚,这个标识过程有什么意义。

直接看flush0方法:

1、如果当前selectionKey 是写事件,说明有线程执行flush过程,则直接返回。
2、否则直接执行flush操作。

1、如果当前socketChannel已经关闭,或断开连接,则执行失败操作。
2、否则执行doWrite把数据写入到socketChannel。

1、size方法返回outboundBuffer有多少Entry实例。
2、in.nioBuffers()负责把Entry中保存的ByteBuf类型的msg,重新返回Nio的ByteBuffer实例,并返回ByteBuffer数组nioBuffers,其实msg和ByteBuffer实例指向的是同一块内存,因为在UnpooledDirectByteBuf实现类中,已经维护了ByteBuffer的实例。
3、socketChannel.write()方法把nioBuffers的数据写到socket中,这是Nio中的实现。

到此为止,nioBuffers的数据都flush到socket,客户端可以准备接收了。

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

1 1 收藏 评论

关于作者:占小狼

我是占小狼。在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。如果读完觉得有收获的话,记得关注和点赞哦。非要打赏的话,我也是不会拒绝的。 个人主页 · 我的文章 · 9 ·  

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部