Netty 源码深度解析(九) - 编码(下)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Netty 源码深度解析

设置写状态

1.png

  • 统计当前有多少字节需要需要被写出

1.png


  • 当前缓冲区中有多少待写字节

1.png
2.png
3.png

4.png

5.png

  • 所以默认不能超过64k

1.png
2.png

  • 自旋锁+CAS 操作,通过 pipeline 将事件传播到channelhandler 中监控

1.png


flush:刷新buffer队列

添加刷新标志并设置写状态


  • 不管调用channel.flush(),还是ctx.flush(),最终都会落地到pipeline中的head节点

1.png


  • 之后进入到AbstractUnsafe

1.png


  • flush方法中,先调用
  • 1.png
    2.png
    3.png
    4.png
  • 结合前面的图来看,上述过程即

首先拿到 unflushedEntry 指针,然后将flushedEntry指向unflushedEntry所指向的节点,调用完毕后

1.png

遍历 buffer 队列,过滤bytebuf

  • 接下来,调用 flush0()

1.png

  • 发现这里的核心代码就一个 doWrite

1.png

AbstractNioByteChannel


  • 继续跟
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
    int writeSpinCount = -1;
    boolean setOpWrite = false;
    for (;;) {
        // 拿到第一个需要flush的节点的数据
        Object msg = in.current();
        if (msg instanceof ByteBuf) {
            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) {
                    setOpWrite = true;
                    break;
                }
                flushedAmount += localFlushedAmount;
                if (!buf.isReadable()) {
                    done = true;
                    break;
                }
            }
            in.progress(flushedAmount);
            // 写完之后,将当前节点删除
            if (done) {
                in.remove();
            } else {
                break;
            }
        } 
    }
}
  • 第一步,调用current()先拿到第一个需要flush的节点的数据

1.png

  • 第二步,拿到自旋锁的迭代次数

1.png

  • 第三步 调用 JDK 底层 API 进行自旋写

自旋的方式将ByteBuf写到JDK NIO的Channel

强转为ByteBuf,若发现没有数据可读,直接删除该节点


1.png


  • 拿到自旋锁迭代次数

1.png

  • 在并发编程中使用自旋锁可以提高内存使用率和写的吞吐量,默认值为16
  • 1.png
  • 继续看源码

2.png

  • javaChannel(),表明 JDK NIO Channel 已介入此次事件
    1.png
    2.png
  • 得到向JDK 底层已经写了多少字节

1.png

2.png

  • 从 Netty 的 bytebuf 写到 JDK 底层的 bytebuffer

1.png

2.png

  • 第四步,删除该节点

节点的数据已经写入完毕,接下来就需要删除该节点


1.png

首先拿到当前被flush掉的节点(flushedEntry所指)

然后拿到该节点的回调对象 ChannelPromise, 调用 removeEntry()移除该节点

1.png

这里是逻辑移除,只是将flushedEntry指针移到下个节点,调用后

1.png

随后,释放该节点数据的内存,调用safeSuccess回调,用户代码可以在回调里面做一些记录,下面是一段Example

1.png

最后,调用 recycle,将当前节点回收


writeAndFlush - 写队列并刷新

writeAndFlush在某个Handler中被调用后,最终会落到 TailContext节点

1.png
2.png
3.png
4.png

通过一个boolean变量flush,表明调用invokeWriteAndFlush or invokeWriteinvokeWrite便是我们上文中的write过程。

1.png

可以看到,最终调用的底层方法和单独调用writeflush一样的


1.png

2.png

由此看来,invokeWriteAndFlush基本等价于write之后再来一次flush


总结


  • 调用write并没有将数据写到Socket缓冲区中,而是写到了一个单向链表的数据结构中,flush才是真正的写出
  • writeAndFlush等价于先将数据写到netty的缓冲区,再将netty缓冲区中的数据写到Socket缓冲区中,写的过程与并发编程类似,用自旋锁保证写成功
  • netty中的缓冲区中的ByteBuf为DirectByteBuf

如何把对象变成字节流,最终写到socket底层?


当 BizHandler 通过 writeAndFlush 方法将自定义对象往前传播时,其实可以拆分成两个过程


  • 通过 pipeline逐渐往前传播,传播到其中的一个 encode 节点后,其负责重写 write 方法将自定义的对象转化为 ByteBuf,接着继续调用 write 向前传播
  • pipeline中的编码器原理是创建一个ByteBuf,将Java对象转换为ByteBuf,然后再把ByteBuf继续向前传递,若没有再重写了,最终会传播到 head 节点,其中缓冲区列表拿到缓存写到 JDK 底层 ByteBuffer


目录
相关文章
|
2天前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
2天前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
21天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
3天前
|
安全 搜索推荐 数据挖掘
陪玩系统源码开发流程解析,成品陪玩系统源码的优点
我们自主开发的多客陪玩系统源码,整合了市面上主流陪玩APP功能,支持二次开发。该系统适用于线上游戏陪玩、语音视频聊天、心理咨询等场景,提供用户注册管理、陪玩者资料库、预约匹配、实时通讯、支付结算、安全隐私保护、客户服务及数据分析等功能,打造综合性社交平台。随着互联网技术发展,陪玩系统正成为游戏爱好者的新宠,改变游戏体验并带来新的商业模式。
|
存储 缓存 NoSQL
跟着源码学IM(十一):一套基于Netty的分布式高可用IM详细设计与实现(有源码)
本文将要分享的是如何从零实现一套基于Netty框架的分布式高可用IM系统,它将支持长连接网关管理、单聊、群聊、聊天记录查询、离线消息存储、消息推送、心跳、分布式唯一ID、红包、消息同步等功能,并且还支持集群部署。
13531 1
|
7月前
|
消息中间件 Oracle Dubbo
Netty 源码共读(一)如何阅读JDK下sun包的源码
Netty 源码共读(一)如何阅读JDK下sun包的源码
144 1
|
NoSQL Java Redis
跟着源码学IM(十二):基于Netty打造一款高性能的IM即时通讯程序
关于Netty网络框架的内容,前面已经讲了两个章节,但总归来说难以真正掌握,毕竟只是对其中一个个组件进行讲解,很难让诸位将其串起来形成一条线,所以本章中则会结合实战案例,对Netty进行更深层次的学习与掌握,实战案例也并不难,一个非常朴素的IM聊天程序。 原本打算做个多人斗地主练习程序,但那需要织入过多的业务逻辑,因此一方面会带来不必要的理解难度,让案例更为复杂化,另一方面代码量也会偏多,所以最终依旧选择实现基本的IM聊天程序,既简单,又能加深对Netty的理解。
176 1
|
7月前
|
编解码 前端开发 网络协议
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
Netty Review - ObjectEncoder对象和ObjectDecoder对象解码器的使用与源码解读
171 0
|
7月前
|
编解码 安全 前端开发
Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读
Netty Review - StringEncoder字符串编码器和StringDecoder 解码器的使用与源码解读
264 0

热门文章

最新文章

推荐镜像

更多