Netty 5.0源码分析-ByteBuf
1. 概念
Java NIO API自带的缓冲区类功能相当有限,没有经过优化,使用JDK的ByteBuffer操作更复杂。故而Netty的作者Trustin Lee为了实现高效率的网络传输,重新造轮子,Netty中的ByteBuf实际上就相当于JDK中的ByteBuffer,其作用是在Netty中通过Channel传输数据。
2. 优势
- 可以自定义缓冲类型;
- 通过内置的复合缓冲类型,实现透明的零拷贝(zero-copy);
- 不需要调用flip()来切换读/写模式;
- 读取和写入索引分开;
- 方法链;
- 引用计数;
- Pooling(池)。
3. 实现机制
ByteBuf实际上是在一个抽象的字节数组byte[]
上进行读/写操作的集合。它提供了两个指针变量用来支持读写操作:readerIndex
和writerIndex
。下图展现了如何将一个buffer利用两个指针来划分为三个区域。
由此可见,ByteBuf真正可读取的内容长度是writerIndex - readerIndex
。
围绕着读和写操作,接下来分析ByteBuf的实现逻辑。
3.1 读操作
读操作主要提供以下功能:
- readByte:取1字节的内容;
- readBoolean:取1字节的内容,返回
readByte() != 0
; - readUnsignedByte:取1字节的内容,返回(
(short) (readByte() & 0xFF)
);(能把负数转换为无符号吗?) - readShort:取2字节的内容,返回转换后的
short
类型; - readUnsignedShort:取2字节的内容,返回
readShort() & 0xFFFF
; - readMedium:取3字节的内容,返回转换后的
int
类型; - readUnsignedMedium:取3字节的内容,返回转换后的
int
类型; - readInt:取4字节的内容;
- readUnsignedInt:取4字节的内容,返回
readInt() & 0xFFFFFFFFL
; - readLong:取8字节的内容;
- readChar:取1字节的内容;
- readFloat:取4字节的int内容,转换为
float
类型; - readDouble:取8字节的long内容,转换为
double
类型; - readBytes:取指定长度的内容,返回
ByteBuf
类型; - readSlice:取指定长度的内容,返回
ByteBuf
类型; - readBytes:取指定长度的内容到目标容器。
3.2 写操作
写操作提供的功能主要是往ByteBuf中写入byte内容,不再一一赘述。主要区别在于写入前根据类型转换为相对应长度的byte数组。
主要函数是:writeBoolean、writeByte、writeShort、writeMedium、writeInt、writeLong、writeChar、writeFloat、writeDouble、writeBytes、writeZero。
3.3 边界值安全
不论读或写,肯定会存在ByteBuf数据为空或满的情形,作为数据容器,要存在边界值检查,确保读写安全。
可读检查
首先调用ensureAccessible()
方法来检查ByteBuf对象是否被引用,如果其引用计数器为0,代表该对象将要被释放,已失效,那么抛出IllegalReferenceCountException
异常,确保不能够执行接下来的操作;
然后进行读取参数合法性检查,如不合法,则抛出响应的异常。
实现代码框架如下:
protected final void checkReadableBytes(int minimumReadableBytes) {
ensureAccessible();
if (minimumReadableBytes < 0) {//参数为负,抛出参数非法异常
throw new IllegalArgumentException();
}
if (readerIndex > writerIndex - minimumReadableBytes) {
//容器中可读字节不够,抛出越界异常
throw new IndexOutOfBoundsException();
}
}
可写检查
进行待写入长度的合法性检查,如不合法,这抛出相应的异常。如果待写入小于可写的长度,则正常返回然;否则,进行容量的扩展,并确保容量是2的指数幂。
实现代码框架如下:
public ByteBuf ensureWritable(int minWritableBytes) {
if (minWritableBytes < 0) {
throw new IllegalArgumentException();
}
if (minWritableBytes <= writableBytes()) {
return this;
}
if (minWritableBytes > maxCapacity - writerIndex) {
throw new IndexOutOfBoundsException();
}
// 扩展现在的容量大小直到2的指数幂大小
int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);
// 调整容器到新的容量大小
capacity(newCapacity);
return this;
}
在函数calculateNewCapacity()中,主要判断参数minNewCapacity与阈值threshold(4M)关系:
- 相等,则返回threshold;
- 大于,则按阈值(4MB)递增,返回增加后新容量newCapacity;
- 小于,newCapacity的初始大小设置为64字节,每次翻倍,直到newCapacity大于minNewCapacity为止,返回
Math.min(newCapacity, maxCapacity)
。
注:maxCapacity
是使用者自己设定的ByteBuf容量上限。
4. 继承层次
ByteBuf的类结构图如下所示:
4.1 AbstractByteBuf
实现ByteBuf的一个骨架,提供容器的上层操作实现,具体的读写内容需要依赖于具体的ByteBuf实现类。
AbstractByteBuf有两个实现类:AbstractDerivedByteBuf
和AbstractReferenceCountedByteBuf
。
4.1.1 AbstractDerivedByteBuf
ByteBuf的抽象基类,实现了包装另一个ByteBuf功能。在AbstractByteBuf的基础上提供了一下功能:
- refCnt:获得该对象的引用计数;
- retain:增加该对象的引用计数(无参数:+1;有参数:+指定的increment);
- release:减少该对象的引用计数(无参数:-1;有参数:-指定的increment),当引用计数减少到0时,释放该对象。返回值为true,当且仅当引用计数变为0和该对象已释放。
- internalNioBuffer:内部实现就是简单的调用
nioBuffer(index, length)
; - nioBuffer:得到内部buffer的一个区域包装,即得到buffer的子区域作为NIO ByteBuffer,返回的ByteBuffer内容不会再受到原buffer索引或内容改变的影响。
AbstractDerivedByteBuf的派生类如下所示:
下面逐个分析下派生类的具体功能实现:
DuplicatedByteBuf
派生类buffer,简单的把所有的数据访问请求发送给内部的buffer。推荐使用ByteBuf.duplicate()
来创建该对象,而不是直接调用本身的构造函数。
对象与内部的buffer共享该buffer整个区域的缓冲数据,只不过它们饿单独保持自己的索引标记。
ReadOnlyByteBuf
派生类buffer,将原有的ByteBuf包装为制度的ByteBuf,所有的写请求都将被禁止。推荐使用Unpooled.unmodifiableBuffer(ByteBuf)
来创建该对象,而不是直接调用本身的构造函数。
对象与内部的buffer使用相同的索引标记,即共享readerIndex
和writerIndex
。
SlicedByteBuf
派生类buffer,仅暴露内部buffer的一个子区域,即切片。推荐使用ByteBuf.slice()
或ByteBuf.slice(int, int)
来创建该对象,而不是直接调用本身的构造函数。
4.1.2 AbstractReferenceCountedByteBuf
ByteBuf的抽象基类,实现了引用计数功能。
提供一个volatile
类型的整型变量refCnt
来记录引用次数。
主要的功能函数是refCnt、retain、release,用来更新引用次数refCnt
。
AbstractReferenceCountedByteBuf的派生类如下所示:
下面逐个分析下派生类的具体功能实现:
CompositeByteBuf
它是一个虚拟的buffer,将多个buffer合并为一个buffer,默认最大可以合并的buffer个数为DEFAULT_MAX_COMPONENTS
= 16。推荐使用ByteBufAllocator.compositeBuffer()
或Unpooled.wrappedBuffer(ByteBuf...)
,而不是直接调用本身的构造函数。
(合并操作还需仔细阅读源码,待完成)
FixedCompositeByteBuf
功能和CompositeByteBuf
相似,只是以只读的方式合并一个ByteBuf数组。
在功能实现中,对于所有的可能更改buffers数组的set操作,均抛出ReadOnlyBufferException
异常。
PooledByteBuf
Pooled的基类, 提供Pool的基本实现。
PooledByteBuf有三个衍生类:
- PooledDirectByteBuf:提供池化的直接内存支持,基于 NIO ByteBuffer;
- PooledHeapByteBuf:提供池化的堆内存支持, 基于byte[];
- PooledUnsafeDirectByteBuf:提供池化的直接内存支持, 基于 NIO ByteBuffer。读写数据依赖于PlatformDependent,为了得到最佳性能,通过final类
Unsafe
来进行数据的读写。
ReadOnlyByteBufferBuf
包装一个只读的ByteBuffer,所有的set方法均抛出ReadOnlyBufferException
异常。
它具有一个衍生类ReadOnlyUnsafeDirectByteBuf
:
在ReadOnlyByteBufferBuf
的基础上提供对direct ByteBuffer的支持,为了得到最佳性能,通过final类Unsafe
来进行数据的读写。
UnpooledDirectByteBuf
提供非池化的直接内存支持, 基于 NIO ByteBuffer。推荐使用Unpooled.directBuffer(int)
和Unpooled.wrappedBuffer(ByteBuffer)
来替代本身的构造函数来生成对象。
它具有一个衍生类ThreadLocalPooledByteBuf
。
UnpooledHeapByteBuf
提供非池化的堆内存支持, 基于byte[]。采用大端序来存储数据。
UnpooledUnsafeDirectByteBuf
提供非池化的直接内存支持,基于 NIO ByteBuffer。读写数据依赖于PlatformDependent,为了得到最佳性能,通过final类Unsafe
来进行数据的读写。推荐使用Unpooled.directBuffer(int)
和Unpooled.wrappedBuffer(ByteBuffer)
来替代本身的构造函数来生成对象。
4.2 EmptyByteBuf
ByteBuf的直接实现类,构建一个容量和最大容量均为0的空ByteBuf。
它不能容纳数据,对于所有的get、set、read和write操作均抛出IndexOutOfBoundsException
异常。
4.3 ReplayingDecoderBuffer
ByteBuf的直接实现类,buffer的数据读取采用ReplayingDecoder
机制,它的原理是阻塞IO,当没有读取到足够的数据时,会抛出REPLY异常,然后进入循环 while (in.isReadable())
不断检查是否有足够的数据,直到读取到足够的数据放入到ReplayingDecoderBuffer对象中。
4.4 SwappedByteBuf
转换字节序的ByteBuf包装类,主要功能是交换内部ByteBuf的字节序列,即大端序BIG_ENDIAN
和小端序LITTLE_ENDIAN
之间的转换。
4.5 WrappedByteBuf
ByteBuf的包装类,将所有方法调用委派给被包装的ByteBuf对象。
其派生类如下图所示:
4.5.1 AdvancedLeakAwareByteBuf
为了方便监控ByteBuf的泄露,AdvancedLeakAwareByteBuf的所有方法都添加了leak.record()
,用来记录调用者当前栈的踪迹,从而ResourceLeakDetector
就可以发现最后被访问的泄漏资源是哪个。
4.5.2 SimpleLeakAwareByteBuf
实现方式和AdvancedLeakAwareByteBuf
非常相似,但只对order
方法添加了资源泄露的检查动作leak.record()
。
4.5.3 UnreleasableByteBuf
包装其它的buffer到一个ByteBuf,用于阻止用户增加或减少这个包装buffer的引用计数,防止他人对ByteBuf的销毁动作。
一般的使用场景就是定义特殊的常量ByteBuf,然后包装成unreleasableBuffer()后就不怕被其他人错误的销毁掉:
public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToMessageEncoder<Object> {
private static final ByteBuf CRLF_BUF = unreleasableBuffer(directBuffer(CRLF.length).writeBytes(CRLF));
}
(END)
Netty 5.0源码分析-ByteBuf的更多相关文章
- Netty 5.0源码分析之综述
1. 前言 本系列主要是用于梳理Netty的架构流程,深入设计细节,重点关注Netty是如何实现它所声称的特性. (ps:本人水平有限,如有错误,请不吝指教 : )) 2. 什么是Netty Nett ...
- Netty 5.0源码分析-Bootstrap
1. 前言 io.netty.bootstrap类包提供包含丰富API的帮助类,能够非常方便的实现典型的服务器端和客户端通道初始化功能. 包含的接口类: //提供工厂类的newChannel方法创建一 ...
- Netty 核心组件 Pipeline 源码分析(二)一个请求的 pipeline 之旅
目录大纲: 前言 针对 Netty 例子源码做了哪些修改? 看 pipeline 是如何将数据送到自定义 handler 的 看 pipeline 是如何将数据从自定义 handler 送出的 总结 ...
- AFNetWorking3.0源码分析
分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end
- Solr5.0源码分析-SolrDispatchFilter
年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...
- Solr4.8.0源码分析(25)之SolrCloud的Split流程
Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...
- Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)
Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...
- Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)
Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...
- Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)
Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及P ...
随机推荐
- Crystal Report 在 VS 2010 中的使用和发布
原文:Crystal Report 在 VS 2010 中的使用和发布 使用: 打开CrystalReport官网下载页 目前最新版本为13.0.4 选择“SAP Crystal Reports, v ...
- asp.net如何实现word文档在线预览
原文:asp.net如何实现word文档在线预览 实现方式:office文档转html,再在浏览器里面在线浏览 1.首先引入com组件中office库,然后在程序集扩展中引入word的dll 2.将M ...
- jquery 超简单的点赞效果
1.HTML(可以优化一下,尽量少些几个标签.....) <div id="dianz"> <b class="cz"><em&g ...
- Hypeiron Planning/Essbase修改规划类型名称
1.修改planning关系库 1.1--修改Plan_type,例如将type_name “Plan1”修改为”Plan1ts”,提交 SELECT * FROM hsp_plan_type FOR ...
- 在 InstantRails 环境下,安装使用 redMine
在 InstantRails 环境下,安装使用 redMine 分类: Redmine2009-06-01 10:35 732人阅读 评论(0) 收藏 举报 characterrailsencodin ...
- NET Framework 4.5新特性 数据库的连接加密保护。
NET Framework 4.5新特性 (一) 数据库的连接加密保护. NET Framework 4.5 ado.net数据库连接支持使用SecureString内存流方式保密文本. 一旦使用这 ...
- OAUTH协议简介
OAUTH协议简介 原文来自:http://blog.csdn.net/hereweare2009/article/details/3968582 分类: Open API2009-03-08 12: ...
- Redis简介与简单安装
Redis简介与简单安装 一.NoSQL的风生水起 1.1 后Web2.0时代的发展要求 随着互联网Web2.0网站的兴起,传统的关系数据库在应付Web2.0网站,特别是超大规模和高并发的SNS类 ...
- Cygwin 是一个用于 Windows 的类 UNIX shell 环境
cygwin的安装使用 Cygwin 是一个用于 Windows 的类 UNIX shell 环境. 它由两个组件组成:一个 UNIX API 库,它模拟 UNIX 操作系统提供的许多特性:以及 ...
- Json.Net6.0
Json.Net6.0入门学习试水篇 前言 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.简单地说,JSON 可以将 JavaScript 对象中表 ...