傳統 BIO(Blocking I/O)採用「一請求一線程」模型,連接數增長時線程開銷巨大。NIO(Non-blocking I/O / New I/O)引入 Channel、Buffer、Selector 三大核心組件,配合 Reactor 模式,實現「少量線程處理海量連接」,是 Netty、Tomcat NIO、Jetty 等高性能網路框架的基石。
| 概念 | 對比 | 本質 |
|---|---|---|
| 同步/異步 | 應用與內核交互 | 同步:發起調用後等待/輪詢;異步:發起後做別事,完成時收到通知 |
| 阻塞/非阻塞 | 調用線程狀態 | 阻塞:調用不返回直到就緒;非阻塞:立即返回狀態碼 |
| NIO 定位 | 同步非阻塞 | 應用主動輪詢 Selector,就緒時讀寫 |
| AIO (NIO.2) | 異步非阻塞 | 內核完成讀寫後回調(CompletionHandler) |
Reactor 將「事件分發」與「業務處理」解耦,核心角色三件套:
Selector,循環 select(),分發就緒事件OP_ACCEPT,建立連接並註冊 OP_READOP_READ/OP_WRITE,讀取解碼→業務邏輯→編碼寫入public class Reactor implements Runnable {
private final Selector selector;
private final ServerSocketChannel serverSocket;
public Reactor(int port) throws IOException {
selector = Selector.open();
serverSocket = ServerSocketChannel.open();
serverSocket.socket().bind(new InetSocketAddress(port));
serverSocket.configureBlocking(false); // 關鍵:非阻塞模式
// 註冊 ACCEPT 事件,綁定 Acceptor
SelectionKey key = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
key.attach(new Acceptor(this));
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
selector.select(); // 阻塞等待事件
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
dispatch(key); // 分發給附屬的 Runnable
it.remove();
}
}
} catch (IOException e) { e.printStackTrace(); }
}
void dispatch(SelectionKey key) {
Runnable r = (Runnable) key.attachment();
if (r != null) r.run();
}
}
public class Acceptor implements Runnable {
private final Reactor reactor;
public Acceptor(Reactor reactor) { this.reactor = reactor; }
@Override
public void run() {
try {
SocketChannel channel = reactor.serverSocket.accept();
if (channel != null) {
// 創建 Handler 並註冊 READ 事件
new SocketReadHandler(reactor.selector, channel);
}
} catch (IOException e) { e.printStackTrace(); }
}
}
public class SocketReadHandler implements Runnable {
private final SocketChannel channel;
public SocketReadHandler(Selector selector, SocketChannel channel) throws IOException {
this.channel = channel;
channel.configureBlocking(false);
// 註冊到同一 Selector,綁定自己為 attachment
SelectionKey key = channel.register(selector, 0);
key.attach(this);
key.interestOps(SelectionKey.OP_READ);
selector.wakeup(); // 喚醒 select() 處理新註冊
}
@Override
public void run() {
ByteBuffer buffer = ByteBuffer.allocate(1024);
try {
int read = channel.read(buffer);
if (read > 0) {
buffer.flip();
// 解碼請求 → 提交業務線程池處理
// requestHandle(new Request(channel, buffer));
} else if (read == -1) {
channel.close(); // 對端關閉
}
} catch (IOException e) { e.printStackTrace(); }
}
}
| 痛點 | 優化方案 |
|---|---|
| 單 Reactor 瓶頸 | 主從 Reactor:MainReactor 接收,SubReactor 分發 I/O |
| 業務阻塞 I/O 線程 | 業務線程池隔離,I/O 線程只負責讀寫 |
| Buffer 頻繁分配 | PooledByteBufAllocator 池化管理 |
| 空輪詢 (CPU 100%) | epoll 空輪詢修復、rebuildSelector |
| 半包/黏包 | LengthFieldBasedFrameDecoder 等解碼器 |
文中提到的「汽車工廠」比喻精準對應:
NIO 的本質是「事件驅動 + 狀態機」,Reactor 模式將並發控制從「線程同步」轉移到「事件分發」。理解 Selector、Channel、Buffer 的交互細節,以及 Acceptor/Handler 的職責劃分,是掌握 Netty、掌握高性能網路編程的必經之路。