Netty In Action中文版 - 第五章:Buffers(缓冲)
本章介绍
- ByteBuf
- ByteBufHolder
- ByteBufAllocator
- 使用这些接口分配缓冲和运行操作
每当你须要数据传输时,它必须包括一个缓冲区。Java NIO API自带的缓冲区类是相当有限的,没有经过优化,使用JDK的ByteBuffer操作更复杂。缓冲区是一个重要的组建,它是API的一部分。
Netty提供了一个强大的缓冲区实现用于表示一个字节序列,并帮助你操作原始字节或自己定义的POJO。Netty的ByteBuf相当于JDK的ByteBuffer,ByteBuf的作用是在Netty中通过Channel数据传输。它被又一次设计以解决JDK的ByteBuffer中的一些问题,从而使开发者开发网络应用程序显得更有效率。本章将讲述Netty中的缓冲区,并了解它为什么比JDK自带的缓冲区实现更优秀。还会深入了解在Netty中使用ByteBuf訪问数据以及怎样使用它。
5.1 Buffer API
- ByteBuf
- ByteBufHolder
Netty使用reference-counting(引用计数)的时候知道安全释放Buf和其它资源,尽管知道Netty有效的使用引用计数。这都是自己主动完毕的。这同意Netty使用池和其它技巧来加高速度和保持内存利用率在正常水平,你不须要做不论什么事情来实现这一点。可是在开发Netty应用程序时,你应该处理数据尽快释放池资源。
- 能够自己定义缓冲类型
- 通过一个内置的复合缓冲类型实现零拷贝
- 扩展性好,比方StringBuffer
- 不须要调用flip()来切换读/写模式
- 读取和写入索引分开
- 方法链
- 引用计数
- Pooling(池)
5.2 ByteBuf - 字节数据容器
因为各种原因,一个高效、方便、易用的数据接口是必须的,而Netty的ByteBuf满足这些需求,ByteBuf是一个非常好的经过优化的数据容器。我们能够将字节数据有效的加入到ByteBuf中或从ByteBuf中获取数据。ByteBuf有2部分:一个用于读,一个用于写。我们能够按顺序的读取数据,而且能够跳到開始又一次读一遍。全部的数据操作,我们仅仅须要做的是调整读取数据索引和再次開始读操作。
5.2.1 ByteBuf怎样在工作?
開始读字节后,读取索引添加。你能够读取字节,直到写入索引和读取索引处理同样的位置,次数若继续读取。则会抛出IndexOutOfBoundsException。调用ByteBuf的不论什么方法開始读/写都会单独维护读索引和写索引。ByteBuf的默认最大容量限制是Integer.MAX_VALUE,写入时若超出这个值将会导致一个异常。
下图显示了一个容量为16的ByteBuf:
5.2.2 不同类型的ByteBuf
直接缓冲区不会占用堆空间容量,使用时应该考虑到应用程序要使用的最大内存容量以及怎样限制它。直接缓冲区在使用Socket传递数据时性能非常好,由于若使用间接缓冲区,JVM会先将数据拷贝到直接缓冲区再进行传递;可是直接缓冲区的缺点是在分配内存空间和释放内存时比堆缓冲区更复杂。而Netty使用内存池来解决这种问题。这也是Netty使用内存池的原因之中的一个。直接缓冲区不支持数组訪问数据,可是我们能够间接的訪问数据数组,如以下代码:
- ByteBuf directBuf = Unpooled.directBuffer(16);
- if(!directBuf.hasArray()){
- int len = directBuf.readableBytes();
- byte[] arr = new byte[len];
- directBuf.getBytes(0, arr);
- }
訪问直接缓冲区的数据数组须要很多其它的编码和更复杂的操作。建议若须要在数组訪问数据使用堆缓冲区会更好。
下图显示CompositeByteBuf组成header和body:
- CompositeByteBuf compBuf = Unpooled.compositeBuffer();
- ByteBuf heapBuf = Unpooled.buffer(8);
- ByteBuf directBuf = Unpooled.directBuffer(16);
- //加入ByteBuf到CompositeByteBuf
- compBuf.addComponents(heapBuf,directBuf);
- //删除第一个ByteBuf
- compBuf.removeComponent(0);
- Iterator<ByteBuf> iter = compBuf.iterator();
- while(iter.hasNext()){
- System.out.println(iter.next().toString());
- }
- //使用数组訪问数据
- if(!compBuf.hasArray()){
- int len = compBuf.readableBytes();
- byte[] arr = new byte[len];
- compBuf.getBytes(0, arr);
- }
CompositeByteBuf是ByteBuf的子类。我们能够像操作BytBuf一样操作CompositeByteBuf。而且Netty优化套接字读写的操作是尽可能的使用CompositeByteBuf来做的,使用CompositeByteBuf不会操作内存泄露问题。
5.3 ByteBuf的字节操作
5.3.1 随机訪问索引
ByteBuf使用zero-based-indexing(从0開始的索引),第一个字节的索引是0,最后一个字节的索引是ByteBuf的capacity - 1,以下代码是遍历ByteBuf的全部字节:
- //create a ByteBuf of capacity is 16
- ByteBuf buf = Unpooled.buffer(16);
- //write data to buf
- for(int i=0;i<16;i++){
- buf.writeByte(i+1);
- }
- //read data from buf
- for(int i=0;i<buf.capacity();i++){
- System.out.println(buf.getByte(i));
- }
注意通过索引訪问时不会推进读索引和写索引,我们能够通过ByteBuf的readerIndex()或writerIndex()来分别推进读索引或写索引。
5.3.2 顺序訪问索引
5.3.3 Discardable bytes废弃字节
我们能够调用ByteBuf.discardReadBytes()来回收已经读取过的字节。discardReadBytes()将丢弃从索引0到readerIndex之间的字节。调用discardReadBytes()方法后会变成例如以下图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWJjX2tleQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
5.3.4 可读字节(实际内容)
- ByteBuf buf = Unpooled.buffer(16);
- while(buf.isReadable()){
- System.out.println(buf.readByte());
- }
(代码于原书中有出入。原书可能是基于Netty4之前的版本号解说的,此处基于Netty4)
5.3.5 可写字节Writable bytes
不论什么写的操作会添加writerIndex。若写操作的參数也是一个ByteBuf而且没有指定数据源索引,那么指定缓冲区的readerIndex也会一起添加。若没有足够的可写字节会抛出IndexOutOfBoundException。新分配的缓冲区writerIndex的默认值是0。以下代码显示了随机一个int数字来填充缓冲区,直到缓冲区空间耗尽:
- Random random = new Random();
- ByteBuf buf = Unpooled.buffer(16);
- while(buf.writableBytes() >= 4){
- buf.writeInt(random.nextInt());
- }
5.3.6 清除缓冲区索引Clearing the buffer indexs
下图显示了调用clear()之后:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWJjX2tleQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
5.3.7 搜索操作Search operations
5.3.8 标准和重置Mark and reset
我们能够通过调用readerIndex(int readerIndex)和writerIndex(int writerIndex)移动读索引和写索引到指定位置,调用这两个方法设置指定索引位置时可能抛出IndexOutOfBoundException。
5.3.9 衍生的缓冲区Derived buffers
衍生的缓冲区有独立的readerIndex、writerIndex和标注索引。假设须要现有缓冲区的全新副本。能够使用copy()或copy(int index, int length)获得。看以下代码:
- // get a Charset of UTF-8
- Charset utf8 = Charset.forName("UTF-8");
- // get a ByteBuf
- ByteBuf buf = Unpooled.copiedBuffer("Netty in Action rocks!", utf8);
- // slice
- ByteBuf sliced = buf.slice(0, 14);
- // copy
- ByteBuf copy = buf.copy(0, 14);
- // print "Netty in Action rocks!"
- System.out.println(buf.toString(utf8));
- // print "Netty in Act"
- System.out.println(sliced.toString(utf8));
- // print "Netty in Act"
- System.out.println(copy.toString(utf8));
5.3.10 读/写操作以及其它一些操作
- get/set操作以索引为基础,在给定的索引设置或获取字节
- 从当前索引開始读写,递增当前的写索引或读索引
ByteBuf的各种读写方法或其它一些检查方法能够看ByteBuf的源代码。这里不赘述了。
5.4 ByteBufHolder
ByteBufHolder是一个辅助类,是一个接口,事实上现类是DefaultByteBufHolder,另一些实现了ByteBufHolder接口的其它接口类。ByteBufHolder的作用就是帮助更方便的訪问ByteBuf中的数据,当缓冲区没用了后,能够使用这个辅助类释放资源。
ByteBufHolder非常easy,提供的可供訪问的方法也非常少。假设你想实现一个“消息对象”有效负载存储在ByteBuf,使用ByteBufHolder是一个好主意。
以下会介绍一些Netty中的缓冲区工具类。
5.4.1 ByteBufAllocator
其它方法的使用能够看ByteBufAllocator源代码及凝视。
- ServerBootstrap b = new ServerBootstrap();
- b.group(group).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
- .childHandler(new ChannelInitializer<SocketChannel>() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- // get ByteBufAllocator instance by Channel.alloc()
- ByteBufAllocator alloc0 = ch.alloc();
- ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- //get ByteBufAllocator instance by ChannelHandlerContext.alloc()
- ByteBufAllocator alloc1 = ctx.alloc();
- ctx.writeAndFlush(buf.duplicate()).addListener(ChannelFutureListener.CLOSE);
- }
- });
- }
- });
Netty有两种不同的ByteBufAllocator实现。一个实现ByteBuf实例池将分配和回收成本以及内存使用降到最低;还有一种实现是每次使用都创建一个新的ByteBuf实例。
Netty默认使用PooledByteBufAllocator,我们能够通过ChannelConfig或通过引导设置一个不同的实现来改变。很多其它细节在后面讲述。
5.4.2 Unpooled
看以下代码:
- //创建复合缓冲区
- CompositeByteBuf compBuf = Unpooled.compositeBuffer();
- //创建堆缓冲区
- ByteBuf heapBuf = Unpooled.buffer(8);
- //创建直接缓冲区
- ByteBuf directBuf = Unpooled.directBuffer(16);
5.4.3 ByteBufUtil
5.5 Summary
Netty In Action中文版 - 第五章:Buffers(缓冲)的更多相关文章
- Netty In Action中文版 - 第四章:Transports(传输)
本章内容 Transports(传输) NIO(non-blocking IO,New IO), OIO(Old IO,blocking IO), Local(本地), Embedded(嵌入式) U ...
- Netty In Action中文版 - 第七章:编解码器Codec
http://blog.csdn.net/abc_key/article/details/38041143 本章介绍 Codec,编解码器 Decoder,解码器 Encoder,编码器 Netty提 ...
- Netty In Action中文版 - 第六章:ChannelHandler
本章介绍 ChannelPipeline ChannelHandlerContext ChannelHandler Inbound vs outbound(入站和出站) 接受连接或创建他们仅仅是你的应 ...
- Netty In Action中文版 - 第三章:Netty核心概念
在这一章我们将讨论Netty的10个核心类.清楚了解他们的结构对使用Netty非常实用.可能有一些不会再工作中用到.可是也有一些非经常常使用也非常核心,你会遇到. Bootstrap ...
- Java Persistence with MyBatis 3(中文版) 第五章 与Spring集成
MyBatis-Spring是MyBatis框架的子模块,用来提供与当前流行的依赖注入框架Spring的无缝集成. Spring框架是一个基于依赖注入(Dependency Injection)和面向 ...
- Netty In Action中文版 - 第一章:Netty介绍
本章介绍 Netty介绍 为什么要使用non-blocking IO(NIO) 堵塞IO(blocking IO)和非堵塞IO(non-blocking IO)对照 Java NIO的问题和在Nett ...
- Netty In Action中国版 - 第二章:第一Netty程序
本章介绍 获得Netty4最新的版本号 设置执行环境,以构建和执行netty程序 创建一个基于Netty的server和client 拦截和处理异常 编制和执行Nettyserver和client 本 ...
- Netty In Action中文版 - 第十五章:选择正确的线程模型
http://blog.csdn.net/abc_key/article/details/38419469 本章介绍 线程模型(thread-model) 事件循环(EventLoop) 并发(Con ...
- Learning Spark中文版--第五章--加载保存数据(2)
SequenceFiles(序列文件) SequenceFile是Hadoop的一种由键值对小文件组成的流行的格式.SequenceFIle有同步标记,Spark可以寻找标记点,然后与记录边界重新 ...
随机推荐
- HDU1004题解分析(字符串处理)
这道题是从上个星期开始做的,看到题时觉得似曾相似,好像做过,理了一下思路敲完代码又不对,后来发现是数组用错了,之后又重新想了数组和比较用法,昨天改了一个多小时,后来样例输出全部正确,所有情况都考虑到了 ...
- dzz使用总结(添加云盘,好用的Web文件管理器,网络播放器)
dzz添加云盘: http://www.lebook.me/book/22822#fid_3990471 呆萌http://pan.diemoe.net/s/GcdFI4 网络播放器 mediaele ...
- NOIP2015
现在来总结一下. 斗地主 这题的题目描述感觉不太清晰,当时有很多人去问,但都没有得到任何回应.好吧,虽然我也是似懂非懂,但是就算看清楚了题目又能怎么样呢. 首先这题只能够搜索吧,或者说是DP,不过有很 ...
- 【jQuery】使用JQ来编写面板的淡入淡出效果
本文与上一篇的<[jQuery]使用JQ来编写最主要的淡入淡出效果>(点击打开链接)为姊妹篇. 但上一篇仅仅是对文本的基本控制,本篇则是对面板元素进行控制. 尽管功能上很类似,可是所用到的 ...
- C#学习之------委托
1.向窗体中添加控件的最少步骤: 窗体的句柄为this private System.Windows.Forms.Button button1; ...
- DOM操作HTML文档
概述 之前写过一些关于DOM方法的知识,理论方法的偏多,所以,这篇博客主要是实践方面的Demo,如果,大家觉得理论方面有所欠缺,大家可以看看这几篇博客:JavaScript总结(一.基本概念)和Jav ...
- Android线程和handler
根据视频仿照着写了个demo: package com.wyl.wylthreadtest; import android.graphics.Color; import android.os.Bund ...
- Redis 突然报错 NOAUTH Authentication required
查找相关资料,说是添加了密码 只需要在redis的配置文件redis.conf中开启requirepass就可以了,比如我设置我的访问密码是mypassword requirepass mypassw ...
- __get __set 实例
<?php class Person { //下面是人的成员属性,都是封装的私有成员 private $name; //人的名子 private $sex; //人的性别 private $ag ...
- media_root以及static_root配置
# At the top of settings/base.pyfrom os.path import join, abspath, dirnamehere = lambda *x: join(abs ...