使用通用程式庫
Netty 是用於建構網路應用的架構,但它也提供適用於其他用途的基礎類別,甚至是不執行 socket I/O 的程式。
io.netty.buffer
提供一個稱為 ByteBuf
的通用緩衝區類型。它就像 java.nio.ByteBuffer
,但速度更快、使用者更友善且可擴充。
您是否曾忘記呼叫 java.nio.ByteBuffer.flip()
並困惑緩衝區中為何沒有任何內容?這種情況在 ByteBuf
中絕不會發生,因為它有兩個索引,一個用於讀取,一個用於寫入
ByteBuf buf = ...;
buf.writeUnsignedInt(42);
assertThat(buf.readUnsignedInt(), is(42));
它具有一組更豐富的存取方法,可更輕鬆地存取緩衝區的內容。例如,它具有已簽署和未簽署整數、搜尋和字串的存取方法。
您無法繼承 java.nio.ByteBuffer
,但您可以使用 ByteBuf
。還提供一個抽象骨架實作以方便您使用。因此,您可以撰寫自己的緩衝區實作,例如檔案備份、複合實作,甚至混合實作。
當分配新的 java.nio.ByteBuffer
時,其內容會填入零。此「歸零」會消耗 CPU 週期和記憶體頻寬。通常,緩衝區會隨後立即從某個資料來源填入,因此歸零沒有好處。
為了回收,java.nio.ByteBuffer
會依賴 JVM 垃圾回收器。對於堆疊緩衝區而言,這樣做沒問題,但直接緩衝區則不行。根據設計,預期直接緩衝區可以使用很長一段時間。因此,分配許多生命週期較短的直接 NIO 緩衝區會經常造成 OutOfMemoryError
。此外,使用 (隱藏的、專有的) API 明確地配置直接緩衝區的速度也不怎麼快。
ByteBuf
的生命週期與其參照計數有關。當其計數降為零時,其基礎記憶體區域(byte[]
或直接緩衝區) 會明確地取消參照、配置或傳回至池。
Netty 也提供一個純粹的緩衝池實作,而不會浪費 CPU 週期或記憶體頻寬來歸零緩衝區
ByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
ByteBuf buf = alloc.directBuffer(1024);
...
buf.release(); // The direct buffer is returned to the pool.
然而,參照計數並非萬靈丹。如果 JVM 在其基礎記憶體區域傳回至池之前回收了池化的緩衝區,則外洩最終會耗盡池。
為了幫助您解決外洩問題,Netty 提供靈活的外洩偵測機制,讓您可以在應用程式的效能和外洩報告的詳細程度之間取得平衡。如需更多資訊,請參閱 參考計數物件。
非同步地執行任務 (排程任務並在完成時取得通知) 的情形很常見,而且應該很輕鬆容易。當 java.util.concurrent.Future
首次出現時,我們興奮之情並未持續太久。我們必須「封鎖」才能在完成時取得通知。在非同步程式設計中,您會在完成時指定「要執行什麼動作」,而不是等待結果。
io.netty.concurrent.Future
是 JDK Future
的子類別。它讓您可以新增一位聆聽者,該聆聽者會在未來完成時由事件迴圈呼叫。
io.netty.util.concurrent.EventExecutor
是單執行緒事件迴圈,用來延伸 java.util.concurrent.ScheduledExecutorService
。您可以建立自己的事件迴圈或將其用作功能豐富的任務執行器。通常,您會建立多個 EventExecutor
以利用平行性
EventExecutorGroup group = new DefaultEventExecutorGroup(4); // 4 threads
Future<?> f = group.submit(new Runnable() { ... });
f.addListener(new FutureListener<?> {
public void operationComplete(Future<?> f) {
..
}
});
...
有時您會想要一個獨一無二的執行器,該執行器總是可用,而且不需要生命週期管理。GlobalEventExecutor
是單執行緒的單例 EventExecutor
,其執行緒會在延遲後啟動,並在暫時沒有已排程的任務時停止。
GlobalEventExecutor.INSTANCE.execute(new Runnable() { ... });
在內部,Netty 使用它來通知其他 EventExecutor
終止。
請注意,此功能僅供內部使用。如果需求充足,我們考慮將它移出函式庫中。
io.netty.util.internal.PlatformDependent
提供與平台相關且可能不安全的作業。您可以將其視為覆蓋 sun.misc.Unsafe
和其他與平台相關的專有 API 的薄層。
為了建置效能很高的網路應用程式架構,我們引進了公用程式。您可能會發現一些有用程式。
如果您的執行緒執行時間很長而且您配置同一個類型的許多生命週期短的物件,您可以使用執行緒區域物件池,稱為 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();
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
定義 ChannelOption
,讓非核心傳輸機制可以用類型安全的模式定義傳輸機制特定的選項。
使用 io.netty.util.AttributeMap
介面可以獲得快速、類型安全、執行緒安全的鍵值對集合
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
是 Constant
。
雜湊輪計時器是 java.util.Timer
和 java.util.concurrent.ScheduledThreadPoolExecutor
的可擴充替代方案。它可以有效地處理許多已排程工作和取消這些工作,如下表所示
排程新工作 | 取消工作 | |
---|---|---|
HashedWheelTimer |
O(1) | O(1) |
java.util.Timer 和 ScheduledThreadPoolExecutor |
O(logN) | O(logN),其中 N = 已排程的工作數 |
在內部,它使用雜湊表,它的金鑰是工作的計時功能,進而讓大多數的計時作業都能在恆定時間內得到處理。(java.util.Timer
使用二元堆。)
若要進一步瞭解雜湊輪計時器,請參閱 這份投影片檔(「雜湊和階層式計時輪」,Dharmapurikar) 和 這篇論文(「雜湊和階層式計時輪:實作計時器功能的有效資料結構」,Varghese 和 Lauck)。
以下類別很有用,但在其他函式庫(例如 Guava)中,您可以找到不錯的替代方案
-
io.netty.util.CharsetUtil
提供一般常見的java.nio.charset.Charset
。 -
io.netty.util.NetUtil
提供一般常見的網路相關常數,例如 IPv4 和 IPv6 回授位址的InetAddress
。 -
io.netty.util.DefaultThreadFactory
是通用ThreadFactory
實作,讓您可以輕鬆地設定執行緒執行緒。
由於 Netty 盡量減少其依賴關係,因此某些實用程式類別與其他熱門函式庫中的類別類似,例如 Guava。
此類函式庫提供各種實用程式類別和替代資料類型,降低使用 JDK API 的難度,且通常表現良好。
Netty 專注於提供建構用於以下項目:
- 非同步程式設計
- 低階作業(又稱為「機械同情」),例如:
- 非堆積存取
- 存取專有內部作業
- 與平台相關的行為
有時,Java 會透過採用吸收 Netty 提供建構的構想來進步。例如,JDK 8 新增了 CompletableFuture
,與 io.netty.util.concurrent.Future
有些重疊。在此案例中,Netty 的建構提供良好的移轉途徑;我們會勤加更新 API,並考慮未來的移轉。