Netty---相关
http://www.infoq.com/cn/articles/netty-million-level-push-service-design-points/
ChannelOption用到的socket的标准参数
-
ChannelOption.SO_BACKLOG, 1024
BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。如果未设置或所设置的值小于1,Java将使用默认值50。
-
ChannelOption.SO_KEEPALIVE, true
是否启用心跳保活机制。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。
-
ChannelOption.TCP_NODELAY, true
在TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认。为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据。这里就涉及到一个名为Nagle的算法,该算法的目的就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块。
TCP_NODELAY就是用于启用或关于Nagle算法。如果要求高实时性,有数据发送时就马上发送,就将该选项设置为true关闭Nagle算法;如果要减少发送次数减少网络交互,就设置为false等累积一定大小后再发送。默认为false。
4.ChannelOption.SO_REUSEADDR, true
SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则bind时将出错。 SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。 SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。 SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)
5.ChannelOption.SO_RCVBUF AND ChannelOption.SO_SNDBUF
定义接收或者传输的系统缓冲区buf的大小,
6.ChannelOption.ALLOCATOR
Netty4使用对象池,重用缓冲区
bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
ByteBuf
http://blog.csdn.net/SpiderDog/article/category/1800249
ByteBuf是Netty框架里最重要的类之一,简单的说,ByteBuf就是Java.nio.ByteBuffer的Netty版。
ByteBuf逻辑结构
正如类名所反映出来的,ByteBuf逻辑上就是一个byte容器。ByteBuf里的数据被两个指针划分为三个部分,如下图所示:
- reader index前面的数据是已经读过的数据,这些数据可以扔掉
- 从reader index开始,到writer index之前的数据是可读数据
- 从writer index开始,为可写区域
正是因为这样的设计,ByteBuf可以同时读写数据(只要可读区域和可写区域都还有空闲空间),而java.nio.ByteBuffer则必须调用flip()方法才能从写状态切换到读状态。
ByteBuf API
ByteBuf提供了大量的方法,比较常用的有下面这些:
- writeXxx(xxx value) 这组方法将不同类型的数据写到buf里,同时将writerIndex往前移适当的距离
- readXxx() 这组方法从buf里读出某种类型的数据,同时将readerIndex往前移适当的距离
- skipBytes(int length) 将readerIndex往前移指定的距离
- setXxx(int index, xxx value) 这组方法将不同类型的数据写到buf的指定位置
- getXxx(int index) 这组方法从buf的指定位置读出一个某种类型的数据
- readerIndex()/writerIndex() 访问readerIndex和writerIndex
- readerIndex(int)/writerIndex(int) 设置readerIndex和writerIndex
- readableBytes() 返回可读区域的字节数
- writableBytes() 返回可写区域的字节数
- clear() 清除buf(把readerIndex和writerIndex都设为0)
- discardReadBytes() 扔掉已读数据
值得一提的是,discardReadBytes()方法需要把可读数据移动到buf的开头,因此是个比较慢的操作。而clear()方法只是将两个指针清0,所以相对而言速度很快。
ByteBufAllocator - 抽象工厂模式
在Netty的世界里,ByteBuf实例通常应该由ByteBufAllocator来创建。ByteBuf和Allocator的关系如下图所示:
Allocator的buffer()方法创建ByteBuf实例,ByteBuf的alloc()方法返回创建自己的Allocator。ByteBufAllocator的实现使用了抽象工厂模式,如下图所示:
CompositeByteBuf - 组合模式
CompositeByteBuf可以让我们把多个ByteBuf当成一个大Buf来处理,ByteBufAllocator提供了compositeBuffer()工厂方法来创建CompositeByteBuf。CompositeByteBuf的实现使用了组合模式,如下图所示:
ByteBufInputStream - 适配器模式
ByteBufInputStream使用适配器模式,使我们可以把ByteBuf当做Java的InputStream来使用。同理,ByteBufOutputStream允许我们把ByteBuf当做OutputStream来使用。
比如说我们要实现一个自定义的消息协议,消息包括header和body两部分内容,body里放的是JSON字符串。那么就可以使用ByteBufInputStream来避免把ByteBuf里的字节拷贝到字节数组的开销:
- private Object decodeMessage(ByteBuf bb) {
- // read header
- // bb.readXxx()...
- // read body
- InputStreamReader reader = new InputStreamReader(new ByteBufInputStream(bb));
- return new Gson().fromJson(reader, JsonObject.class);
- }
ReadOnlyByteBuf - 装饰器模式
ReadOnlyByteBuf用适配器模式把一个ByteBuf变为只读,ReadOnlyByteBuf通过调用Unpooled.unmodifiableBuffer(ByteBuf)方法获得:
类似的ByteBuf适配器还包括:
ByteBuf - 工厂方法模式
前面也提到过了,我们很少需要直接通过构造函数来创建ByteBuf实例,而是通过Allocator来创建。从装饰器模式可以看出另外一种获得ByteBuf的方式是调用ByteBuf的工厂方法,比如:
- ByteBuf#duplicate()
- ByteBuf#slice()
ByteBufProcessor - 访问者模式还是迭代器模式?
最后,ByteBuf提供了4个forEachByte()方法来对ByteBuf里的数据进行某种处理或查找,看起来像是访问者模式和迭代器模式的混合:
- public abstract int forEachByte(ByteBufProcessor processor);
- public abstract int forEachByte(int index, int length, ByteBufProcessor processor);
- public abstract int forEachByteDesc(ByteBufProcessor processor);
- public abstract int forEachByteDesc(int index, int length, ByteBufProcessor processor);