Netty3从2016年的3.10.6.Final之后就再没有新版本了,公司项目用的netty3,最近在升级为netty4,下面我们先了解下netty3与netty4的不同之处吧。
项目结构的改变
Netty的包名从 org.jboss.netty 改成 io.netty, 因为 我们不再是JBoss.org的一份子了.
二进制的 JAR 文件被分成多个模块, 这样用户可以从类路径中排除不需要的特性. 当前的结构如下:
netty-parent Maven parent POM
netty-common Utility classes and logging facade
netty-buffer ByteBuf API that replaces java.nio.ByteBuffer
netty-transport Channel API and core transports
netty-transport-rxtx Rxtx transport
netty-transport-sctp SCTP transport
netty-transport-udt UDT transport
netty-handler Useful ChannelHandler implementations
netty-codec Codec framework that helps write an encoder and a decoder
netty-codec-http Codecs related with HTTP, Web Sockets, SPDY, and RTSP
netty-codec-socks Codecs related with SOCKS protocol
netty-all All-in-one JAR that combines all artifacts above
netty-tarball Tarball distribution
netty-example Examples
netty-testsuite-* A collection of integration tests
netty-microbench Microbenchmarks
现在所有的 artifacts (除了 netty-all.jar) 都实现了 OSGi bundle, 可以用在你的 OSGi 容器中.
通用 API 的改变
Netty中大部分的操作都支持链式方法,简化了操作
不能配置的getter 不再有get- 前缀. (例如 Channel.getRemoteAddress() → Channel.remoteAddress())
布尔属性仍然保留 is- 前缀, 避免造成迷惑 (例如 ‘empty’ 既是形容词也是动词, 所以 empty() 有两个意思)
4.0 CR4 和 4.0 CR5之间的 改变请参照 Netty 4.0.0.CR5 released with new-new API
Buffer API 的改变
ChannelBuffer → ByteBuf
由于上面提到的项目结构的改变, buffer API可以作为一个独立的包使用. 即使你对使用Netty作为网络应用框架不感兴趣,你也可以使用buffer API.
因此, 类型名ChannelBuffer 不再合适了, 所以改名为 ByteBuf.
用来创建新buffer的工具类 ChannelBuffers被分成了两个工具类: Unpooled
和 ByteBufUtil
. 从名字Unpooled上也能猜出, 4.0引入了池化
的 ByteBuf, 可以通过 ByteBufAllocator
的具体实现来分配(allocated).
ByteBuf 不是一个接口,而是一个抽象类class
根据我们内部的性能测试,将ByteBuf 从接口改为抽象类可以给总吞吐量带来5%的提升.
大部分的buffer变成了动态的,具有可配置的最大容量
在3.x, buffer可以是固定大小或者动态的,固定大小的buffer一旦创建容量就不能改变。 而动态buffer的容量在write*(…)方法需要更多空间时可以改变。
自4.0开始, 所有的buffer都是动态的。 然而,它们要比以前的动态buffer更好。你可以更容易更安全地增加或者减少buffer的容量。因为提供了新的方法 ByteBuf.capacity(int newCapacity)所以
改变容量更容易。之所以说它安全, 是因为你可以设置一个最大容量,这样buffer就不会不限制的增长.
// No more dynamicBuffer() - use buffer().
ByteBuf buf = Unpooled.buffer();
// Increase the capacity of the buffer.
buf.capacity(1024);
...
// Decrease the capacity of the buffer (the last 512 bytes are deleted.)
buf.capacity(512);
唯一的例外是那些使用wrappedBuffer方法包装的(wrapped)一个单一的buffer或者一个单一字节数组。你不能增加它的容量, 因为这样会使包装一个已有buffer的目的(节省内存复制)失去意义。
如果你想改变这样的buffer的容量, 你应该使用你需要的容量创建一个新的buffer, 然后将原来包装的buffer中的数据复制到这个新的buffer中.
新的buffer类型: CompositeByteBuf
一个名叫CompositeByteBuf
的buffer实现为composite buffer实现定义了多个高级操作。用户可以使用composite buffer节省大量的内存复制,只比随机访问buffer的代价大一点。
为了创建一个新的composite buffer, 可以像以前一样使用Unpooled.wrappedBuffer(…) , 也可以使用Unpooled.compositeBuffer(…), 或者 ByteBufAllocator.compositeBuffer().
可预知的NIO buffer转型
在3.x中, ChannelBuffer.toByteBuffer() 以及它的变体所提供的约定并不那么明确。用户无法确定这些方法会返回一个拥有共享数据的视图buffer还是一个拥有独立数据的通过复制得到的buffer(a view buffer with shared data or a copied buffer with separate data)。
4.0 中使用 ByteBuf.nioBufferCount(), nioBuffer(), 和 nioBuffers()代替toByteBuffer(). 如果nioBufferCount() 返回 0, 用户总是可以通过copy().nioBuffer()得到一个复制buffer.
对小端序(Little endian)支持的改变
小端序(Little endian)
的支持做了很大的改变。先前用户为了得到小端序buffer,可以通过 LittleEndianHeapChannelBufferFactory 或者按指定的字节序包装一个已有的buffer:
4.0 中增加了一个新的方法: ByteBuf.order(ByteOrder), 它返回当前buffer对象的一个具有指定字节序的视图:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.ByteOrder;
ByteBuf buf = Unpooled.buffer(4);
buf.setInt(0, 1);
// Prints '00000001'
System.out.format("%08x%n", buf.getInt(0));
ByteBuf leBuf = buf.order(ByteOrder.LITTLE_ENDIAN);
// Prints '01000000'
System.out.format("%08x%n", leBuf.getInt(0));
assert buf != leBuf;
assert buf == buf.order(ByteOrder.BIG_ENDIAN);
池化buffer Pooled buffers
Netty 4引入了一个高性能的buffer池
, 它是 jemalloc 的变种, 组合了 buddy allocation 和 slab allocation的功能. 有以下好处:
- 减少了 GC 压力,因为使用unpooled buffer会带来频繁的内存分配和回收
- 减少了内存的带宽消耗(memory bandwidth consumption) 因为新的内存不可避免地要用zero填充(初始化)
- 定时回收 direct buffers
为了使用这个特性, 除非用户非要得到unpooled buffer, 他应该通过 ByteBufAllocator
分配buffer:
Channel channel = ...;
ByteBufAllocator alloc = channel.alloc();
ByteBuf buf = alloc.buffer(512);
....
channel.write(buf);
ChannelHandlerContext ctx = ...
ByteBuf buf2 = ctx.alloc().buffer(512);