SelectionKey 是 Channel 向 Selector 註冊的憑證。關鍵操作:
// 註冊時綁定 Connection 物件,避免額外 Map 查找
key = socketChannel.register(selector, SelectionKey.OP_READ);
key.attach(this); // this = Connection 實例
// 事件迴圈中取回
Connection conn = (Connection) key.attachment();
ByteBuffer.allocate(512) (Heap Buffer) 而非 Direct Buffer,因連線生命週期短、緩衝區小,Heap 分配較快且由 GC 管理。sc.read(buffer) 可能返回 0 (無資料) 或部分資料。需累積至讀完整行 (以 \n 判斷) 才解析。FileChannel 對應靜態檔案,呼叫 prepareForResponse()。private void prepareForResponse() throws IOException {
// 1. 組裝 HTTP 標頭至 ByteBuffer
StringBuffer hdr = new StringBuffer(128);
hdr.append("HTTP/1.0 200 OK\r\nContent-Length: ").append(fileSize).append("\r\n\r\n");
responseLineBuffer = ByteBuffer.wrap(hdr.toString().getBytes("ASCII"));
// 2. 關鍵:切換關注事件為 OP_WRITE,並喚醒 Selector
key.interestOps(SelectionKey.OP_WRITE);
key.selector().wakeup(); // 確保 Selector 立即從 select() 返回處理寫入
}
public void writeResponse() throws IOException {
// 先寫標頭
int written = socketChannel.write(responseLineBuffer);
if (responseLineBuffer.hasRemaining()) return; // 標頭未寫完,下次繼續
// 再傳檔案內容:零拷貝 transferTo
if (fileChannel != null && filePosition < fileSize) {
long transferred = fileChannel.transferTo(filePosition, Math.min(64*1024, fileSize - filePosition), socketChannel);
filePosition += transferred;
}
// 全部完成 -> 關閉
if (filePosition >= fileSize && !responseLineBuffer.hasRemaining()) {
close();
}
}
r:並發請求總數s:請求檔案大小 (bytes)b:客戶端頻寬 (bytes/sec)t:BIO 伺服器執行緒池大小 (固定)c:單請求固定 CPU 處理開銷 (秒),含解析、系統呼叫等 (假設 10ms)前提:伺服器頻寬、磁碟速度、CPU 均非瓶頸;客戶端頻寬為限制因素。
t 個請求,分批次處理。
r 連線,無批次等待。
當 r → ∞,d 線性增長。若 t=100,r=10000 並發,NIO 快 100 倍。
| 場景 | 檔案大小 | 客戶端頻寬 | 連線時間 s/b | BIO 表現 | NIO 優勢 |
|---|---|---|---|---|---|
| 廣域網下載大檔 | 1 MB | 8 KB/s | 128 秒 | 執行緒長期佔用 | 126 倍 (c=10ms, t=100) |
| 區域網小檔 | 10 KB | 10 MB/s | 1 ms | 執行緒快速釋放 | 約 10% (CPU 開銷主導) |
| 長連線 Keep-Alive | 持續 1 小時 | 閒置 | 3600 秒 | 執行緒永久佔用 | 無限大 (BIO 直接耗盡執行緒) |
Connection 物件,增加 GC 壓力 (HotSpot 年輕代回收極快,通常可接受)。synchronized(queue) 交換新連線,高併發下可能成為熱點。解法:多 Acceptor/Selector、無鎖佇列。選 BIO (或虛擬執行緒):業務邏輯複雜、CPU 密集、併發 < 1000、團隊缺乏 NIO 經驗、Java 21+ 可用 Virtual Threads (Project Loom) 以同步寫法獲非阻塞效能。
選 NIO (Netty/Mina/gRPC):高併發靜態資源、代理轉發、長連線推播 (WebSocket/HTTP/2)、頻寬受限慢速客戶端、需精細控制傳輸行為。
現代架構多採用 Netty 等成熟框架屏蔽 NIO 底層細節,兼顧開發效率與極致效能。理解本文模型原理,有助於調優框架參數、排查疑難效能問題。