译者:lijunshu 校对:方腾飞
Netty是一个用来开发基于网络应用的框架,同时也提供了其他与socket I/O无关的基础类。
io.netty.buffer 提供了一个通用的buffer类型ByteBuf类。他与java.nio.ByteBuffer类似,但是更加性能,对用户更友好和可扩展。
当你在调用java.nio.ByteBuffer.flip()时,有没有考虑为什么buffer没有包含所有的信息,他在ByteBuf 从不会发生,因为他有2个index,一个是读取,另一个是写入。
ByteBuf buf = ...;
buf.writeUnsignedInt(42);
assertThat(buf.readUnsignedInt(), is(42));
他拥有丰富的读取方法能更加方便的读取buffer的内容。例如,他拥有方法能读取无符号,有符号数字和字符串。
你无法继承java.nio.ByteBuffer,但是你可以继承ByteBuf。一个抽象的实现可以为你带来方便,你可以基于他做自己的buffer实现,例如基于文件的缓存,buffer组合或者更加复杂的实现。
当一个新的java.nio.ByteBuffer 被分配,他的内容将以零字符串进行填充。零字符串填充会带来CPU和内存的消耗。通常buffer马上会被数据源填充,因此零字符串填充并不是最好的方式。
需要指出的是,java.nio.ByteBuffer是基于JVM内存收集的,他能在Heap内存工作的不错,但是直接内存访问就不行。从设计上说,直接内存访问通常会存活很长一段时间。因此,分配许多短时间直接内存buffer通常会导致OutOfMemoryError,而且,回收一个直接内存所使用的API通常不是很快。
一个ByteBuf的生命周期是基于他被引用的次数,当引用数为0,他底层的内存区(byte[] or direct buffer)将被回收,或者返回内存池。
Netty也提供一种buffer池的实现,并且不浪费CPU和内存来以零来填充。
ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
ByteBuf buf = alloc.directBuffer(1024);
...
buf.release(); // The direct buffer is returned to the pool.
然而,引用数并不是银弹。如果在他底层内存换回内存池之间,JVM就收集了buffer,内存泄漏最终会导致池的资源被耗尽。
为了帮助你调试内存泄漏,Netty提供了一个泄露检测机制能灵活的在你的应用性能和泄露报表之间权衡,更多信息,请参照Reference-counted-objects
执行一个异步任务——调度一个任务,并在任务完成的时候得到通知,应该很普遍而且很方便。当java.util.concurrent.Future 出现,我们并没有激动很久。我们需要阻塞以等待任务完成通知。在异步编程,你需要指定任务完成,你做什么而不是被动等待结果。
io.netty.concurrent.Future是JDK Future的子类,他允许你添加listener,而且当future完成时,这个listener会被eventloop调用。
io.netty.util.concurrent.EventExecutor是继承自java.util.concurrent.ScheduledExecutorService的单线程event loop。你可以建立自己的event loop或者使用一个丰富功能的task executor。通常,你会建立多个EventExecutors来利用计算机的并行计算能力。
EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
Future> f = group.submit(new Runnable() { ... });
f.addListener(new FutureListener> {
public void operationComplete(Future> f) {
..
}
});
...
有些时候,你想要一个唯一的executor,他一直可用并且不需要生命周期管理。GlobalEventExecutoris是一个单线程的EventExecutor 他会在他的线程延迟启动,在没用需要执行的任务后停止。
注意这个功能只是内部使用,我们在考虑在有足够需求的情况下,再把他从internal包中移出。
io.netty.util.internal.PlatformDependent 提供依赖于平台和潜在不安全的操作。你可以认为他是一个在sun.misc.Unsafe 和其他依赖平台API的上面一个薄薄的层。
为了构建一个高效的网络应用框架,我们引入了一些工具,你能找到一些有用的。
如果你的程序现在是长时间运行,而且需要分配许多同类型的短生命周期对象,你可以使用thread local对象池Recycler类。他减少了大量的内存垃圾,节省了内存的消耗和垃圾收集。
public class MyObject {
private static final Recycler RECYCLER = new Recycler() {
protected MyObject newObject(Recycler.Handle handle) {
return new MyObject(handle);
}
}
public static MyObject newInstance(int a, String b) {
MyObject obj = RECYCLER.get();
obj.myFieldA = a;
obj.myFieldB = b;
return obj;
}
private final Recycler.Handle handle;
private int myFieldA;
private String myFieldB;
private MyObject(Handle handle) {
this.handle = handle;
}
public boolean recycle() {
myFieldA = 0;
myFieldB = null;
return handle.recycle(this);
}
}
MyObject obj = MyObject.newInstance(42, "foo");
...
obj.recycle();
对于一些静态的变量,enum非常适合,但是你无法继承他。当你需要在运行时增加更多的变量或者允许第三方定义更多的变量,考虑使用io.netty.util.ConstantPool
public final class Foo extends AbstractConstant {
Foo(int id, String name) {
super(id, name);
}
}
public final class MyConstants {
private static final ConstantPool pool = new ConstantPool() {
@Override
protected Foo newConstant(int id, String name) {
return new Foo(id, name);
}
};
public static Foo valueOf(String name) {
return pool.valueOf(name);
}
public static final Foo A = valueOf("A");
public static final Foo B = valueOf("B");
}
private final class YourConstants {
public static final Foo C = MyConstants.valueOf("C");
public static final Foo D = MyConstants.valueOf("D");
}
Netty使用ConstantPool 来定义ChannelOptions,所以非核心的transports可以以安全的方式定义transport相关的选项。
使用io.netty.util.AttributeMap 接口来定义一个快速,类型安全,线程安全的key-value集合。
public class Foo extends DefaultAttributeMap {
...
}
public static final AttributeKey ATTR_A = AttributeKey.valueOf("A");
public static final AttributeKey ATTR_B = AttributeKey.valueOf("B");
Foo o = ...;
o.attr(ATTR_A).set("foo");
o.attr(ATTR_B).set(42);
就像你已经知晓的,AttributeKey 是一个常量。
HashedWheelTimer是一个可扩展的,可用于替代java.util.Timer 或者java.util.concurrent.ScheduledThreadPoolExecutor。他可以处理许多计划的任务,并且他很容易取消任务。
foo | Schedule a new task | Cancel a task |
---|---|---|
HashedWheelTimer | O(1) | O(1) |
java.util.Timer and ScheduledThreadPoolExecutor | O(logN) | O(logN) where N = number of pending tasks |
在它的内部,使用一个Hash Table,大多数timer的操作,Hash Table的主键存放调度任务到yield的常数时间(it uses a hash table whose key is a task's timing to yield constant time for most timer operations)。(java.util.Timer 使用二进制堆。)
更多有关hashed wheel timer,请参阅these slides ("Hashed and Hierarchical Timing Wheels," Dharmapurikar)and this paper ("Hashed and Hierarchical Timing Wheels: Data Structures for the Efficient Implementation of a Timer Facility," Varghese and Lauck)。
下面的类也非常有用,但是你可以在其他类例如Guava中找到类似的。
io.netty.util.CharsetUtil 提供常用的java.nio.charset.Charsets
io.netty.util.NetUtil 提供常用的基于网络的常量例如IPv4和IPv6的回送地址。
io.netty.util.DefaultThreadFactory 是一个通用的ThreadFactory 实现,方便你配置线程池。
因为Netty设法最小化依赖,它的许多工具类与其它的常用库类似,例如Guava。
这样的库提供了多样的工具类和其它的数据类型,这使我们使用JDK少些痛苦,而且这样的库也做的不错。
Netty关注于提供
异步编程
底层操作
堆外内存访问
访问底层进行操作
平台独立操作
Java有时采取并加入了Netty的一些类。例如,JDK 8 加入了CompleteFuture,这与io.netty.util.concurrent.Future相重叠,在这样的情况下Netty提供了很好的移植方式。在API的开发上,我们考虑移植性。