1
1
ByteBuf - 字节数据的容器
2
2
====
3
3
4
- 网络进行交互时,需要以字节码发送/接收数据。由于各种原因,一个高效 、方便、易用的数据接口是必须的 ,而 Netty 的 ByteBuf 满足这些需求。
4
+ 因为所有的网络通信最终都是基于底层的字节流传输,因此一个高效 、方便、易用的数据接口是必要的 ,而 Netty 的 ByteBuf 满足这些需求。
5
5
6
- ByteBuf 是一个很好的经过优化的数据容器,我们可以将字节数据有效的添加到 ByteBuf 中或从 ByteBuf 中获取数据。ByteBuf 有2部分:一个用于读,一个用于写。我们可以按顺序的读取数据,并且可以跳到开始重新读一遍。所有的数据操作,我们只需要做的是调整读取数据索引和再次开始通过 get() 方法进行读操作 。
6
+ ByteBuf 是一个很好的经过优化的数据容器,我们可以将字节数据有效的添加到 ByteBuf 中或从 ByteBuf 中获取数据。ByteBuf 有2部分:一个用于读,一个用于写。我们可以按顺序的读取数据,也可以通过调整读取数据的索引或者直接将读取位置索引作为参数传递给get方法来重复读取数据 。
7
7
8
8
###ByteBuf 如何在工作?
9
9
10
- 写入数据到 ByteBuf 后,writerIndex(写入索引)增加。开始读字节后,readerIndex(读取索引)增加。你可以读取字节,直到写入索引和读取索引处理相同的位置 ,ByteBuf 变为不可读。当访问数据超过数组的最后位,则会抛出 IndexOutOfBoundsException。
10
+ 写入数据到 ByteBuf 后,writerIndex(写入索引)增加。开始读字节后,readerIndex(读取索引)增加。你可以读取字节,直到写入索引和读取索引处在相同的位置 ,ByteBuf 变为不可读。当访问数据超过数组的最后位,则会抛出 IndexOutOfBoundsException。
11
11
12
- 调用 ByteBuf 的 "read" 或 "write" 开头的任何方法都会提升 相应的索引。另一方面,"set" 、 "get"操作字节将不会移动指数;他们只操作相关的通过参数传入方法的索引 。
12
+ 调用 ByteBuf 的 "read" 或 "write" 开头的任何方法都会提升 相应的索引。另一方面,"set" 、 "get"操作字节将不会移动索引位置;他们只会操作相关的通过参数传入方法的相对索引 。
13
13
14
- ByteBuf 的默认最大容量限制是 Integer.MAX_VALUE,写入时若超出这个值将会导致一个异常 。
14
+ 可以给ByteBuf指定一个最大容量值,这个值限制着ByteBuf的容量。任何尝试将写入索引超过这个值的行为都将导致抛出异常。 ByteBuf 的默认最大容量限制是 Integer.MAX_VALUE。
15
15
16
16
ByteBuf 类似于一个字节数组,最大的区别是读和写的索引可以用来控制对缓冲区数据的访问。下图显示了一个容量为16的空的 ByteBuf 的布局和状态,writerIndex 和 readerIndex 都在索引位置 0 :
17
17
@@ -23,8 +23,8 @@ Figure 5.1 A 16-byte ByteBuf with its indices set to 0
23
23
24
24
####HEAP BUFFER(堆缓冲区)
25
25
26
- 最常用的类型是 ByteBuf 将数据存储在 JVM 的堆空间,这是通过将数据存储在数组的实现。堆缓冲区可以快速分配,当不使用时也可以快速释放。它还提供了直接访问数组的方法,通过 ByteBuf.array() 来获取 byte[ ] 数据。这种方法,清单5.1中示出,是非常适合的情况下
27
- 你必须处理遗留数据 。
26
+ 最常用的模式是 ByteBuf 将数据存储在 JVM 的堆空间,这是通过将数据存储在数组的实现。堆缓冲区可以快速分配,当不使用时也可以快速释放。它还提供了直接访问数组的方法,通过 ByteBuf.array() 来获取 byte[ ] 数据。
27
+ 这种方法,正如清单5.1中所示的那样,是非常适合用来处理遗留数据的 。
28
28
29
29
Listing 5.1 Backing array
30
30
@@ -39,7 +39,7 @@ Listing 5.1 Backing array
39
39
40
40
1.检查 ByteBuf 是否有支持数组。
41
41
42
- 2.如果这样得到的引用数组 。
42
+ 2.如果有的话,得到引用数组 。
43
43
44
44
3.计算第一字节的偏移量。
45
45
@@ -55,16 +55,15 @@ Listing 5.1 Backing array
55
55
####DIRECT BUFFER(直接缓冲区)
56
56
57
57
“直接缓冲区”是另一个 ByteBuf 模式。对象的所有内存分配发生在
58
- 堆,对不对?好吧,并非总是如此。在 JDK1.4 中被引入 NIO 的ByteBuffer 类允许一个 JVM 实现通过本地调用分配内存 ,其目的是
58
+ 堆,对不对?好吧,并非总是如此。在 JDK1.4 中被引入 NIO 的ByteBuffer 类允许 JVM 通过本地方法调用分配内存 ,其目的是
59
59
60
60
* 通过免去中间交换的内存拷贝, 提升IO处理速度;
61
- 直接缓冲区的内容可以驻留的正常垃圾回收以外
62
- 堆。
61
+ 直接缓冲区的内容可以驻留在垃圾回收扫描的堆区以外。
63
62
* DirectBuffer 在 -XX: MaxDirectMemorySize =xxM大小限制下, 使用 Heap 之外的内存, GC对此”无能为力”,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响.(详见< http://docs.oracle.com/javase/7/docs/api/java/nio/ByteBuffer.html. > )
64
63
65
- 这就解释了为什么“直接缓冲区”是理想的 通过 socket 实现数据传输。如果你的数据是包含在堆中分配的缓冲区,JVM 实际上在通过 socket 发送之前将复制您的缓冲区到直接缓冲区 。
64
+ 这就解释了为什么“直接缓冲区”对于那些通过 socket 实现数据传输的应用来说,是一种非常理想的方式。如果你的数据是存放在堆中分配的缓冲区,那么实际上,在通过 socket 发送数据之前,JVM 需要将先数据复制到直接缓冲区 。
66
65
67
- 但是直接缓冲区的缺点是在分配内存空间和释放内存时比堆缓冲区更复杂,另外一个缺点是如果是与传统的代码工作; 因为数据不是在堆上,你可能要作出一个副本 ,如下:
66
+ 但是直接缓冲区的缺点是在内存空间的分配和释放上比堆缓冲区更复杂,另外一个缺点是如果要将数据传递给遗留代码处理, 因为数据不是在堆上,你可能不得不作出一个副本 ,如下:
68
67
69
68
Listing 5.2 Direct buffer data access
70
69
@@ -76,17 +75,17 @@ Listing 5.2 Direct buffer data access
76
75
handleArray(array, 0, length); //5
77
76
}
78
77
79
- 1.检查 ByteBuf 不是由数组支持 。如果不是,这是一个直接缓冲液 。
78
+ 1.检查 ByteBuf 是不是由数组支持 。如果不是,这是一个直接缓冲区 。
80
79
81
80
2.获取可读的字节数
82
81
83
82
3.分配一个新的数组来保存字节
84
83
85
84
4.字节复制到数组
86
85
87
- 5.调用一些参数是 数组,偏移量和长度 的方法
86
+ 5.将数组,偏移量和长度作为参数调用某些处理方法
88
87
89
- 显然,这涉及到比使用支持数组多做一些工作 。因此,如果你知道事先在容器中的数据作为一个数组进行访问 ,你可能更愿意使用堆内存。
88
+ 显然,这比使用数组要多做一些工作 。因此,如果你事前就知道容器里的数据将作为一个数组被访问 ,你可能更愿意使用堆内存。
90
89
91
90
92
91
####COMPOSITE BUFFER(复合缓冲区)
@@ -97,7 +96,7 @@ Netty 提供了 ByteBuf 的子类 CompositeByteBuf 类来处理复合缓冲区
97
96
98
97
* 警告*
99
98
100
- * CompositeByteBuf.hasArray() 总是返回 false,因为它可能包含一些直接或间接的不同类型的 ByteBuf。 *
99
+ * CompositeByteBuf.hasArray() 总是返回 false,因为它可能既包含堆缓冲区,也包含直接缓冲区 *
101
100
102
101
例如,一条消息由 header 和 body 两部分组成,将 header 和 body 组装成一条消息发送出去,可能 body 相同,只是 header 不同,使用CompositeByteBuf 就不用每次都重新分配一个新的缓冲区。下图显示CompositeByteBuf 组成 header 和 body:
103
102
@@ -119,9 +118,9 @@ Listing 5.3 Composite buffer pattern using ByteBuffer
119
118
message2.put(body);
120
119
message2.flip();
121
120
122
- 这种做法显然是低效的;分配和复制操作是不是最佳的,操纵阵列使代码尴尬 。
121
+ 这种做法显然是低效的;分配和复制操作不是最优的方法,操纵数组使代码显得很笨拙 。
123
122
124
- 下面看下 CompositeByteBuf 的版本
123
+ 下面看使用 CompositeByteBuf 的改进版本
125
124
126
125
Listing 5.4 Composite buffer pattern using CompositeByteBuf
127
126
@@ -142,9 +141,8 @@ Listing 5.4 Composite buffer pattern using CompositeByteBuf
142
141
143
142
3.遍历所有 ByteBuf 实例。
144
143
145
- 清单5.4 所示,你可以简单地把一个 CompositeByteBuf 作为一个迭代
146
- 收集器。因为 CompositeByteBuf 不允许到间接数组访问,数据访问,
147
- 见清单5.5,类似于直接缓冲区模式:
144
+ 清单5.4 所示,你可以简单地把 CompositeByteBuf 当作一个可迭代遍历的容器。
145
+ CompositeByteBuf 不允许访问其内部可能存在的支持数组,也不允许直接访问数据,这一点类似于直接缓冲区模式,如图5.5所示。
148
146
149
147
Listing 5.5 Access data
150
148
@@ -156,14 +154,14 @@ Listing 5.5 Access data
156
154
157
155
1.得到的可读的字节数。
158
156
159
- 2.分配一个新的数组,为可读字节长度 。
157
+ 2.分配一个新的数组,数组长度为可读字节长度 。
160
158
161
159
3.读取字节到数组
162
160
163
- 4.使用数组,偏移量和长度作为参数
161
+ 4.使用数组,把偏移量和长度作为参数
164
162
165
163
Netty 尝试使用 CompositeByteBuf 优化 socket I/O 操作,消除
166
- 当可能发生的是与 JDK 实现的性能和内存使用情况的问题。虽然是在Netty 的核心代码进行这种优化,并且是不暴露的,人们应该意识到其影响 。
164
+ 原生 JDK 中可能存在的的性能低和内存消耗问题。虽然这是在Netty 的核心代码中进行的优化,并且是不对外暴露的,但是作为开发者还是应该意识到其影响 。
167
165
168
166
* CompositeByteBuf API*
169
167
0 commit comments