1. 程式人生 > >《Netty官方指南》把Netty當做一個通用的庫

《Netty官方指南》把Netty當做一個通用的庫

原文連結 譯者:lijunshu 校對:方騰飛

Netty是一個用來開發基於網路應用的框架,同時也提供了其他與socket I/O無關的基礎類。

Buffer API

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

Listenable futures and event loops

執行一個非同步任務——排程一個任務,並在任務完成的時候得到通知,應該很普遍而且很方便。當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) {

..

}

});

...

The global event loop

有些時候,你想要一個唯一的executor,他一直可用並且不需要生命週期管理。GlobalEventExecutoris是一個單執行緒的EventExecutor 他會在他的執行緒延遲啟動,在沒用需要執行的任務後停止。

Platform-dependent operations

注意這個功能只是內部使用,我們在考慮在有足夠需求的情況下,再把他從internal包中移出。

io.netty.util.internal.PlatformDependent 提供依賴於平臺和潛在不安全的操作。你可以認為他是一個在sun.misc.Unsafe 和其他依賴平臺API的上面一個薄薄的層。

Other utilities

為了構建一個高效的網路應用框架,我們引入了一些工具,你能找到一些有用的。

Thread-local object pool

如果你的程式現在是長時間執行,而且需要分配許多同類型的短生命週期物件,你可以使用thread local物件池Recycler類。他減少了大量的記憶體垃圾,節省了記憶體的消耗和垃圾收集。

public class MyObject {



private static final Recycler<MyObject> RECYCLER = new Recycler<MyObject>() {

protected MyObject newObject(Recycler.Handle<MyObject> 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<MyObject> handle;

private int myFieldA;

private String myFieldB;



private MyObject(Handle<MyObject> handle) {

this.handle = handle;

}



public boolean recycle() {

myFieldA = 0;

myFieldB = null;

return handle.recycle(this);

}

}



MyObject obj = MyObject.newInstance(42, "foo");

...

obj.recycle();

User-extensible enum

對於一些靜態的變數,enum非常適合,但是你無法繼承他。當你需要在執行時增加更多的變數或者允許第三方定義更多的變數,考慮使用io.netty.util.ConstantPool


public final class Foo extends AbstractConstant<Foo> {

Foo(int id, String name) {

super(id, name);

}

}



public final class MyConstants {



private static final ConstantPool<Foo> pool = new ConstantPool<Foo>() {

@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相關的選項。

Attribute map

使用io.netty.util.AttributeMap 介面來定義一個快速,型別安全,執行緒安全的key-value集合。

public class Foo extends DefaultAttributeMap {

...

}



public static final AttributeKey<String> ATTR_A = AttributeKey.valueOf("A");

public static final AttributeKey<Integer> ATTR_B = AttributeKey.valueOf("B");



Foo o = ...;

o.attr(ATTR_A).set("foo");

o.attr(ATTR_B).set(42);

就像你已經知曉的,AttributeKey 是一個常量。

Hashed wheel timer

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 使用二進位制堆。)

下面的類也非常有用,但是你可以在其他類例如Guava中找到類似的。

  • io.netty.util.CharsetUtil 提供常用的java.nio.charset.Charsets
  • io.netty.util.NetUtil 提供常用的基於網路的常量例如IPv4和IPv6的回送地址。
  • io.netty.util.DefaultThreadFactory 是一個通用的ThreadFactory 實現,方便你配置執行緒池。

與 Guava and JDK8 的比較

因為Netty設法最小化依賴,它的許多工具類與其它的常用庫類似,例如Guava

這樣的庫提供了多樣的工具類和其它的資料型別,這使我們使用JDK少些痛苦,而且這樣的庫也做的不錯。

Netty關注於提供

  • 非同步程式設計
  • 底層操作
  • 堆外記憶體訪問
  • 訪問底層進行操作
  • 平臺獨立操作

Java有時採取並加入了Netty的一些類。例如,JDK 8 加入了CompleteFuture,這與io.netty.util.concurrent.Future相重疊,在這樣的情況下Netty提供了很好的移植方式。在API的開發上,我們考慮移植性。


ebay staff software engineer, working for software development 10+ years for electronic payment and e-commerce, interested in software design, development and new technology.