Zero-Copy
要談到Netty如何實現Non-Blocking I/O (New I/O or NIO)
就得先說明一下Zero-Copy1 2
首先聊聊OS level
資料I/O存取透過buffer機制完成R/W
以下4張圖
上半部為context switches
下半部為copy operations
Copying in Two Sample System Calls
// sample code:
read(file, tmp_buf, len);
write(socket, tmp_buf, len);
由上圖說明下半部cpu copy行為
資料讀出至kernel buffer然後cpu copy至user buffer
再由user buffer將資料cpu copy至socket buffer進行傳送
為了減少重覆的複製搬移
於是有了另一種實作方式mmap
Calling mmap
// sample code:
tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);
kernel buffer和user buffer是shared共享
所以減少了1次的cpu copy行為
之後又有了另一種實作方式sendfile
Replacing Read and Write with Sendfile
// sample code:
sendfile(socket, file, len);
上圖下半部說明了
資料由kernel buffer直接cpu copy至socket buffer而沒有使用user buffer
也因為如此
圖的上半部顯示減少了context switch行為
如果硬體提供gather功能
Gather can assemble data from multiple memory locations, eliminating another copy
kernel buffer append socket buffer
省去了多餘的cpu copy動作
這就是Zero-Copy
Multi-Thread Server
1個client request對應1個server thread處理
(from grizzly-sendfile: Wiki: Algorithms — Project Kenai)
由上圖可知
Thread1, 2, 3分別處理Download1, 2, 3
而且每個thread中會因I/O而出現blocked
而Download4, 5, 6只能排隊等待
直到有thread釋放才會處理
Event-Driven Server
多個client request共享多個server thread處理
(from grizzly-sendfile: Wiki: Algorithms — Project Kenai)
由上圖可知
Download1, 2, 3... 6切割tasks給Thread1, 2, 3同時處理
最後tasks會經過合併回傳結果
Reactor Pattern
(from Netty源码解读(四)Netty与Reactor模式)
Netty NIO其實是Reactor Pattern的實作
dispatcher(selector)傳送message
讓event handler(channelhandler)處理
而Reactor Pattern和Observer Pattern有些許相似及不同之處
請參考
Observer Pattern vs Reactor Pattern
UpStream DownStream
(from Netty の基本)
另外談到寫作的naming
底層實作Netty的有Akka
, Ratpack
, Vert.x
...3
這邊舉Ratpack的<O> Promise<O> transform(Function<? super Upstream<? extends T>,? extends Upstream<O>> upstreamTransformer)
為例
public class Example {
public static void main(String... args) throws Exception {
ExecResult<String> result = ExecHarness.yieldSingle(c ->
Promise.value("foo")
.transform(up -> down ->
up.connect(down.<String>onSuccess(value -> {
try {
down.success(value.toUpperCase());
} catch (Throwable e) {
down.error(e);
}
}))
)
);
assertEquals("FOO", result.getValue());
}
}
可以看到up和down的字眼
其實簡單想就是
up: receive data
down: send data
up接收client訊息成功後轉給down將訊息UpperCase後傳回
以server角度
UpStream -> DownStream
以client角度
DownStream -> UpStream
這只是Netty的channel pipeline handler名詞定義
而Ratpack拿來命名用
以上提的這些
最重要還是得看kernel也就是OS level的支援
請參考
Is Netty's Zero Copy different from OS level Zero Copy?
OS若不支援是無法實現Zero-Copy
當然NIO也無法
另外
想要很簡單的區分async, blocking, non-blocking可以只看這篇的code部份說明
比較 Winsock 環境下 Blocking、Non-Blocking、以及 Asynchronous
Sockets 的不同
應該會有一點想法
最後要說明的是
上述都沒有談到java.nio
或是io.netty.channel
等class
只是稍微分享NIO相關議題
希望未來在使用Netty api時會有點感覺或是幫助