首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java NIO 非阻塞網絡編程:Reactor 模式實戰與 Netty 架構演進

     編輯:Java 相關知識

前言

Java NIO 通過 Selector 實現單線程多路復用,是 Reactor 模式在 Java 的標準實現。本文從 NIO 核心組件入手,構建完整 Reactor 服務端,演進至多 Reactor 多線程架構,揭示 Netty 等高性能框架的設計淵源。

一、NIO 核心組件速覽

組件角色關鍵方法
Selector多路復用器,監聽多 Channel 事件select(), selectedKeys(), wakeup()
Channel雙向數據通道,非阻塞模式register(), read(), write(), accept()
SelectionKeyChannel-Registrar 綁定標識,攜帶附件interestOps(), attachment(), channel()
ByteBuffer數據容器,支援直接內存flip(), compact(), clear(), get()/put()

二、單線程 Reactor 完整實現

2.1 Reactor 主循環


public class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        
        // 註冊 ACCEPT 事件,附件綁定 Acceptor Handler
        SelectionKey sk = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                selector.select(); // 阻塞等待事件
                Set<SelectionKey> selected = selector.selectedKeys();
                for (SelectionKey key : selected) {
                    dispatch(key); // 分發到 Handler
                }
                selected.clear();
            }
        } catch (IOException ex) { /* handle */ }
    }

    void dispatch(SelectionKey key) {
        Runnable handler = (Runnable) key.attachment();
        if (handler != null) handler.run();
    }
}

2.2 Acceptor Handler (連接建立)


class Acceptor implements Runnable {
    public void run() {
        try {
            SocketChannel client = serverSocket.accept();
            if (client != null) {
                // 創建讀寫 Handler,註冊 READ 事件
                new SocketReadHandler(selector, client);
            }
        } catch (IOException ex) { /* handle */ }
    }
}

2.3 SocketReadHandler (讀寫處理)


class SocketReadHandler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    static final int READING = 0, SENDING = 1;
    int state = READING;

    SocketReadHandler(Selector sel, SocketChannel c) throws IOException {
        socket = c;
        socket.configureBlocking(false);
        sk = socket.register(sel, 0); // 初始不關注事件
        sk.attach(this); // 關鍵:附件綁定自己
        sk.interestOps(SelectionKey.OP_READ); // 關注讀事件
        sel.wakeup(); // 喚醒 selector.select()
    }

    public void run() {
        try {
            if (state == READING) readRequest();
            else if (state == SENDING) sendResponse();
        } catch (IOException ex) { /* 關閉連接 */ }
    }

    void readRequest() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = socket.read(buffer);
        if (bytesRead > 0) {
            buffer.flip();
            // 解碼請求 -> 業務處理 (丟入線程池) -> 編碼響應
            // 狀態轉換
            state = SENDING;
            sk.interestOps(SelectionKey.OP_WRITE);
        } else if (bytesRead == -1) {
            socket.close(); // 對端關閉
        }
    }

    void sendResponse() throws IOException {
        // 寫入響應緩衝區
        // 寫完後
        state = READING;
        sk.interestOps(SelectionKey.OP_READ);
    }
}

三、關鍵設計模式解析

  1. 附件模式:SelectionKey.attach(Handler) 實現 Channel 與 Handler 雙向綁定,事件觸發 O(1) 獲取 Handler
  2. 狀態機:Handler 內部 state 控制關注事件切換 (READ ↔ WRITE),避免忙輪詢
  3. wakeup():新註冊 Channel 後喚醒 Selector,避免等待下次 select()
  4. 興趣集動態調整:interestOps() 按需開啟/關閉 OP_READ/OP_WRITE

四、架構演進:主從 Reactor 多線程

4.1 單線程瓶頸

  • 單線程 CPU 核心利用率低
  • 業務處理阻塞事件循環,影響並發

4.2 主從 Reactor 架構


// MainReactor (1 線程) - 只負責 ACCEPT
Selector mainSelector = Selector.open();
serverSocket.register(mainSelector, OP_ACCEPT, mainAcceptorHandler);

// SubReactor 池 (N 線程) - 負責 READ/WRITE/業務分發
Selector[] subSelectors = new Selector[Runtime.getRuntime().availableProcessors()];
ExecutorService workerPool = Executors.newFixedThreadPool(16);

// Acceptor 中輪詢分配 SubReactor
SocketChannel client = serverSocket.accept();
SubReactor nextSub = subReactors[nextIndex++ % subSelectors.length];
nextSub.registerChannel(client); // 跨線程註冊需 wakeup()

4.3 Netty 架構映射

本文組件Netty 對應
MainReactorNioEventLoopGroup bossGroup
SubReactorNioEventLoopGroup workerGroup
HandlerChannelHandler / ChannelPipeline
ByteBufferByteBuf (引用計數、池化、零拷貝)
wakeup()NioEventLoop.register() 內部喚醒

五、生產級優化清單

  1. 直接內存:ByteBuffer.allocateDirect() 減少堆外拷貝
  2. 內存池:避免頻繁 allocate/GC,Netty PooledByteBufAllocator
  3. 零拷貝:文件傳輸 FileChannel.transferTo() / mmap
  4. 批量處理:selector.selectNow() 非阻塞輪詢 + 批量分發
  5. 適應性緩衝區:預測讀取大小動態調整 Buffer 容量
  6. 流控:Channel.config().setAutoRead(false) 手動控制讀取節奏

六、總結

Java NIO Selector + Reactor 模式是高性能網絡服務的基石。從單線程 Demo 到主從 Reactor 多線程,再到 Netty 産業級實現,核心思想始終是:**事件驅動、非阻塞 I/O、線程模型解耦、零拷貝優化**。掌握 Reactor 核心流程與關鍵優化點,即可讀懂 Netty 源碼、排查疑難性能問題、設計自定義協議框架。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved