Netty從0到1系列之Netty邏輯架構【上】

文章目錄

  • 一、Netty邏輯架構【上】
    • 1.1 網絡通信層
      • 1.1.1 BootStrap & ServerBootStrap
        • 1. ?核心方法鏈與配置
        • 2. ? 架構與流程
        • 3. ? 底層實現與原理分析
        • 4. ? 實踐經驗與總結
      • 1.1.2 Channel
    • 1.2 事件調度層
      • 1.2.1 事件調度層概述
      • 1.2.2 EventLoop【事件循環】
      • 1.2.3 EventLoopGroup【事件循環組】
      • 1.2.4 底層實現原理
      • 1.2.5 最佳實踐與經驗總結


推薦閱讀:

【01】Netty從0到1系列之I/O模型
【02】Netty從0到1系列之NIO
【03】Netty從0到1系列之Selector
【04】Netty從0到1系列之Channel
【05】Netty從0到1系列之Buffer(上)
【06】Netty從0到1系列之Buffer(下)
【07】Netty從0到1系列之零拷貝技術
【08】Netty從0到1系列之整體架構、入門程序
【09】Netty從0到1系列之EventLoop
【10】Netty從0到1系列之EventLoopGroup
【11】Netty從0到1系列之Future
【12】Netty從0到1系列之Promise
【13】Netty從0到1系列之Netty Channel
【14】Netty從0到1系列之ChannelFuture
【15】Netty從0到1系列之CloseFuture
【16】Netty從0到1系列之Netty Handler
【17】Netty從0到1系列之Netty Pipeline【上】
【18】Netty從0到1系列之Netty Pipeline【下】
【19】Netty從0到1系列之Netty ByteBuf【上】
【20】Netty從0到1系列之Netty ByteBuf【中】
【21】Netty從0到1系列之Netty ByteBuf【下】


一、Netty邏輯架構【上】

在這里插入圖片描述

Netty 的邏輯處理架構為典型網絡分層架構設計:

  • 網絡通信層
  • 事件調度層
  • 服務編排層

1.1 網絡通信層

網絡通信層的職責是執行網絡 I/O 的操作。它支持多種網絡協議和 I/O 模型的連接操作。當網絡數據讀取到內核緩沖區后,會觸發各種網絡事件,這些網絡事件會分發給事件調度層進行處理。

網絡通信層的核心組件包含BootStrap、ServerBootStrap、Channel三個組件

1.1.1 BootStrap & ServerBootStrap

Bootstrap 是引導的意思,它主要負責整個 Netty 程序的啟動、初始化、服務器連接等過程,它相當于一條主線,串聯了 Netty 的其他核心組件。

  • 用于客戶端引導的 Bootstrap
  • 用于服務端引導的 ServerBootStrap

在這里插入圖片描述

在 Netty 中,所有網絡應用的啟動都是通過 Bootstrap(客戶端)或 ServerBootstrap(服務端)來完成的。它們是工廠類和配置容器的結合體,負責創建、配置并最終啟動 Channel

  • Bootstrap: 用于客戶端。它只需要一個 EventLoopGroup,因為它只需要創建一個單獨的 Channel 來與遠程服務器進行通信。
  • ServerBootstrap: 用于服務端。它需要兩個 EventLoopGroup(通常如此),一個通常被稱為 “Boss”,負責接收傳入的連接;另一個被稱為 “Worker”,負責處理已被接受的連接的流量。
類名用途對應場景
Bootstrap用于啟動 Netty 客戶端發起 TCP 連接(如 HTTP 客戶端、RPC 調用方)
ServerBootstrap用于啟動 Netty 服務端監聽端口,接收客戶端連接(如 Web 服務器、RPC 服務提供方)

? 本質:它們是 Netty 的“啟動器”,封裝了復雜的 NIO 套接字創建、線程模型配置、Channel 初始化等流程,提供流暢式 API(Fluent API),極大簡化了網絡編程。

1. ?核心方法鏈與配置

兩者都通過流暢的鏈式調用(Fluent API)進行配置,核心方法包括:

  • .group(): 設置 EventLoopGroup
  • .channel(): 指定要實例化的 Channel 類(如 NioSocketChannel, NioServerSocketChannel)。
  • .handler(): 為 BootstrapServerBootstrap 本身的 Channel 設置一個 ChannelHandler
  • .childHandler(): (ServerBootstrap 特有) 為 ServerBootstrap 接收的 子 Channel(即代表客戶端連接的 SocketChannel)設置 ChannelHandler
  • .option(): 為 BootstrapServerBootstrap 本身的 Channel 設置底層 TCP 參數。
  • .childOption(): (ServerBootstrap 特有) 為 ServerBootstrap 接收的 子 Channel 設置底層 TCP 參數。
2. ? 架構與流程
  • ServerBootstrapBootstrap 的啟動流程及其核心組件關系:
Bootstrap 啟動流程
初始化
注冊到
Pipeline配置
循環
.groupgroup
Bootstrap
.channelNioSocketChannel
.handler 用于Client Channel
.connecthost, port
NioSocketChannel
Client EventLoop
Client ChannelPipeline
包含handler里添加的處理器
Selector.select 處理READ/WRITE等
ServerBootstrap 啟動流程
初始化
注冊到
循環
有新的連接
注冊到
Pipeline配置
循環
.groupbossGroup, workerGroup
ServerBootstrap
.channelNioServerSocketChannel
.handler 用于ServerChannel
.childHandler 用于子Channel
.bindport
NioServerSocketChannel
Boss EventLoop
Selector.select 等待ACCEPT
創建NioSocketChannel
Worker EventLoop
子ChannelPipeline
包含childHandler里添加的處理器
Selector.select 處理READ/WRITE等
3. ? 底層實現與原理分析

啟動過程分析: ServerBootstrap.bind() 為例,其底層過程非常精妙

  • 創建和初始化Channel

    • 通過反射調用 channelFactory.newChannel() 創建 NioServerSocketChannel 實例。
    • 調用 init(channel) 方法初始化該 Channel。這是最關鍵的一步:
      • .option() 設置的參數應用到 Channel 的 config 上。
      • .handler() 設置的 ChannelHandler 添加到 Channel 的 Pipeline 中。
      • 在 Pipeline 的尾部自動添加一個 ServerBootstrapAcceptor。這個 Handler 是 Netty 內部的“連接接收器”
  • 注冊到 EventLoop

    • 將初始化好的 NioServerSocketChannel 異步地注冊到 BossGroup 中的一個 EventLoop 上。這個過程會提交一個任務到 EventLoop 的線程中執行。

    • 在 EventLoop 線程中,真正的注冊操作是將 Java NIO 的 ServerSocketChannel 注冊到 EventLoop 的 Selector 上,并設置興趣操作為 OP_ACCEPT

  • 綁定端口

    • 注冊成功后,繼續在 EventLoop 線程中調用 Channelbind() 方法,最終調用底層 Java NIO 的 ServerSocketChannel.bind()

    • 綁定成功后,會觸發 channelActive 事件,Pipeline 中的處理器會相應地將 OP_ACCEPT 事件再次注冊到 Selector,確保開始接收連接。

  • 接收連接(ServerBootstrapAcceptor 的作用)

    • 當 BossGroup中的 EventLoop輪詢到 OP_ACCEPT事件時,會調用 NioServerSocketChannel的 read() 方法,其內部通過 ServerSocketChannel.accept()創建一個 Java NIO 的 SocketChannel。
    • Netty 隨后會包裝這個 SocketChannel 為 NioSocketChannel。
    • 關鍵一步: ServerBootstrapAcceptor的 channelRead() 方法會被調用,這個 NioSocketChannel 作為參數傳入。
    • ServerBootstrapAcceptor 會做以下工作:
      • .childOption().childAttr() 設置的參數應用到子 Channel。
      • .childHandler() 中設置的 ChannelInitializer 添加到子 Channel 的 Pipeline 中。
      • 將這個子 Channel 注冊到 WorkerGroup 中的一個 EventLoop 上(負載均衡

至此,新連接的建立和處理職責就從 BossGroup 完全移交給了 WorkerGroup

Bootstrap.connect() 的過程類似,但更簡單,因為它沒有子 Channel 的概念,創建好的 NioSocketChannel 會直接注冊到其唯一的 EventLoopGroup 上,然后發起連接操作。

4. ? 實踐經驗與總結
  1. 線程池配置
  • BossGroup 通常設置為 1。一個線程足以處理所有的連接接收請求,設置過多純屬浪費。
  • WorkerGroup 默認大小是 CPU核心數 * 2。這是一個很好的起點,應根據實際業務是 I/O 密集型 還是 CPU 密集型 進行調整。I/O 密集型可以調大一些。
  1. ChannelOption 配置
  • SO_BACKLOG (Server): 決定了完全建立連接隊列的長度。在高并發場景下需要適當調大。
  • TCP_NODELAY (Client): 建議設置為 true,禁用 Nagle 算法,減少小數據包的發送延遲。
  • SO_KEEPALIVE (Child): 開啟 TCP 層的心跳機制,保證連接存活。
  • SO_RCVBUF/SO_SNDBUF: 根據網絡狀況調整發送和接收緩沖區大小。
  1. 資源管理
  • 始終使用 shutdownGracefully(): 在 finally 塊中優雅關閉 EventLoopGroup,它會等待任務執行完畢并釋放資源。
  • ChannelInitializer 的使用: 在 initChannel 方法中添加 Handler 后,這個 ChannelInitializer 會將自己從 Pipeline 中移除,所以它可以被多個 Channel 安全共享。
  1. Handler 的組織
  • 在 ServerBootstrap 中,.handler() 用于添加處理服務端本身狀態的處理器(如日志、監控),而 .childHandler() 用于添加處理業務邏輯的處理器。
  • 客戶端的 .handler()等同于服務端的 .childHandler()

1.1.2 Channel

表示通道的意思,它是網絡的載體.Channel提供了基本的 API 用于網絡 I/O 操作,如 register、bind、connect、read、write、flush 等.

Netty 自己實現的 Channel 是以 JDK NIO Channel 為基礎的,相比較于 JDK NIO,Netty 的 Channel 提供了更高層次的抽象,同時屏蔽了底層 Socket 的復雜性.

在這里插入圖片描述

  • NioServerSocketChannel 異步 TCP 服務端
  • NioSocketChannel 異步 TCP 客戶端
  • OioServerSocketChannel 同步 TCP 服務端。
  • OioSocketChannel 同步 TCP 客戶端。
  • NioDatagramChannel 異步 UDP 連接。
  • OioDatagramChannel 同步 UDP 連接。

同時Channel還有很多種狀態:

  • 連接建立
  • 連接注冊
  • 數據讀寫
  • 連接銷毀
  • ….

隨著狀態的變化,Channel 處于不同的生命周期,每一種狀態都會綁定相應的事件回調

事件描述
channelRegisteredChannel 創建后被注冊到 EventLoop 上
channelUnregisteredChannel 創建后未注冊或者從 EventLoop 取消注冊
channelActiveChannel 處于就緒狀態,可以被讀寫
channelInactiveChannel 處于非就緒狀態
channelReadChannel 可以從遠端讀取到數據
channelReadCompleteChannel 讀取數據完成

BootStrap 和 ServerBootStrap 分別負責客戶端和服務端的啟動,它們是非常強大的輔助工具類;

Channel 是網絡通信的載體,提供了與底層 Socket 交互的能力

1.2 事件調度層

1.2.1 事件調度層概述

事件調度層的職責是通過 Reactor 線程模型對各類事件進行聚合處理,通過 Selector 主循環線程集成多種事件( I/O 事件、信號事件、定時事件等),實際的業務處理邏輯是交由服務編排層中相關的 Handler 完成。

事件調度層的核心組件包括

  • EventLoopGroup
  • EventLoop

EventLoopGroup 本質是一個線程池,主要負責接收 I/O 請求,并分配線程執行處理請求.

在這里插入圖片描述

  • 一個 EventLoopGroup 往往包含一個或者多個 EventLoop。EventLoop 用于處理 Channel 生命周期內的所有 I/O 事件,如 accept、connect、read、write 等 I/O 事件
    • EventLoopGroup可以包含一個或者多個EventLoop
    • 負責處理Channel生命周期內所有的I/O事件
  • EventLoop 同一時間會與一個線程綁定,每個 EventLoop 負責處理多個 Channel
  • 每新建一個 ChannelEventLoopGroup 會選擇一個 EventLoop 與其綁定
    • 該 Channel 在生命周期內都可以對 EventLoop 進行多次綁定和解綁。

在這里插入圖片描述

EventLoopGroup 的實現類是 NioEventLoopGroup,NioEventLoopGroup 也是 Netty 中最被推薦使用的線程模型

NioEventLoopGroup 繼承于 MultithreadEventLoopGroup,是基于 NIO 模型開發的,可以把 NioEventLoopGroup 理解為一個線程池,每個線程負責處理多個 Channel,而同一個 Channel 只會對應一個線程。

EventLoopGroup 是 Netty 的核心處理引擎,其實 EventLoopGroup 是 Netty Reactor 線程模型的具體實現方式,Netty 通過創建不同的 EventLoopGroup 參數配置,就可以支持 Reactor 的三種線程模型

  • 單線程模型
    • EventLoopGroup 只包含一個 EventLoop,Boss 和 Worker 使用同一個EventLoopGroup;
  • 多線程模型
    • EventLoopGroup 包含多個 EventLoop,Boss 和 Worker 使用同一個EventLoopGroup
  • 主從多線程模型
    • EventLoopGroup 包含多個 EventLoop,Boss 是主 ReactorWorker 是從 Reactor
    • 它們分別使用不同的 EventLoopGroup,主 Reactor 負責新的網絡連接 Channel 創建,然后把 Channel 注冊到從 Reactor。

事件調度層負責監聽網絡連接和讀寫操作,然后觸發各種類型的網絡事件,需要一種機制管理這些錯綜復雜的事件;

1.2.2 EventLoop【事件循環】

EventLoop 是 Netty 的核心抽象,它代表了一個持續運行的、處理所有分配給它的 I/O 事件和任務的線程。你可以將其理解為一個忠誠的管家,它一生只做一件事:在一個無限循環中,不斷地檢查自己負責的“領地”(Channel)上是否有事情需要處理(I/O 事件),或者主人(用戶代碼)是否吩咐了新的任務。

它的核心職責可以簡化為一個公式:

  • EventLoop = Thread + Selector + Task Queue

EventLoop的分配策略:

當 Channel 注冊到 EventLoopGroup 時,EventLoopGroup 會選擇一個 EventLoop 來處理該 Channel 的所有事件:

客戶端1
EventLoopGroup
客戶端2
客戶端3
客戶端4
客戶端5
EventLoop 1
EventLoop 2
EventLoop 3
Channel 1
Channel 4
Channel 2
Channel 5
Channel 3

默認的分配策略是輪詢(Round-Robin),保證 EventLoop 之間的負載均衡。

1.2.3 EventLoopGroup【事件循環組】

EventLoopGroup初始化過程:

  1. 創建線程池:根據指定的線程數創建線程池
  2. 初始化 EventLoop:為每個線程創建對應的 EventLoop
  3. 啟動線程:啟動所有線程,每個線程運行 EventLoop 的 run () 方法
// 簡化的EventLoopGroup初始化邏輯
public NioEventLoopGroup(int nThreads) {this(nThreads, (Executor) null);
}public NioEventLoopGroup(int nThreads, Executor executor) {this(nThreads, executor, SelectorProvider.provider());
}public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider) {super(nThreads, executor, selectorProvider);
}// 父類MultithreadEventLoopGroup的初始化
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}// 父類MultithreadEventExecutorGroup的初始化
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {// 如果未指定線程數,使用默認值:CPU核心數 * 2if (nThreads <= 0) {throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));}// 如果未指定執行器,創建默認執行器if (executor == null) {executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());}// 創建EventLoop數組children = new EventExecutor[nThreads];// 初始化每個EventLoopfor (int i = 0; i < nThreads; i ++) {boolean success = false;try {// 創建EventLoopchildren[i] = newChild(executor, args);success = true;} catch (Exception e) {// 處理異常} finally {// 處理初始化失敗的情況}}// 創建EventLoop選擇器chooser = new GenericEventExecutorChooser(children);// 添加終止監聽器terminationFuture = new DefaultPromise(GlobalEventExecutor.INSTANCE);terminationFuture.addListener(new FutureListener<Object>() {@Overridepublic void operationComplete(Future<Object> future) throws Exception {for (EventExecutor e : children) {e.shutdownGracefully();}}});
}

EventLoopGroup 包含多個 EventLoop,它的主要作用是分配 EventLoop 給新創建的 Channel。它本質是一個 EventLoop 的池子,遵循一種特定的分配策略(通常是輪詢 round-robin)。

EventLoopGroup
EventLoop-1
EventLoop-2
EventLoop-N
Channel-1
Channel-2
Channel-3
Channel-4
  • 一個 EventLoopGroup包含多個 EventLoop
  • 每個 EventLoop綁定一個線程,永不更改
  • 一個 EventLoop可以注冊多個 Channel,但一個 Channel 只能注冊到一個 EventLoop
  • 關鍵:Channel 的所有 I/O 操作都在同一個線程中執行,線程安全,無需額外同步。
EventLoopGroup
EventLoop 1
EventLoop 2
EventLoop 3
...更多EventLoop
Thread 1
Thread 2
Thread 3
...更多Thread
Selector 1
Selector 2
Selector 3
...更多Selector
  • 每個 EventLoopGroup 包含多個 EventLoop
  • 每個 EventLoop 綁定一個 Thread 和一個 Selector
  • EventLoop 的數量默認等于 CPU 核心數
  • 服務端: 通常有兩個 EventLoopGroup
    • BossGroup: 只包含一個或少數幾個 EventLoop,專門負責接收客戶端的連接請求(OP_ACCEPT 事件),并將建立好的連接“轉交”出去。
    • WorkerGroup: 包含多個 EventLoop,負責處理被“轉交”過來的連接上的所有后續 I/O 操作(如 OP_READ, OP_WRITE)。
  • 客戶端: 只需要一個 EventLoopGroup,負責處理所有連接的事件。
一個 EventLoop 的內部細節
提交異步任務
Single Thread
Selector
Task Queue
Channel-1
Channel-2
Channel-...
Runnable Task A
Runnable Task B
EventLoopGroup 內部結構
EventLoopGroup
EventLoop-1
EventLoop-2
EventLoop-...
EventLoop-N
Client Connection 1
Client Connection 2
Client Connection ...
User Code
  • 一個 EventLoopGroup 包含多個 EventLoop
  • 一個 EventLoop 在它的生命周期內只綁定一個線程Thread),這個線程負責處理所有邏輯。
  • 一個 EventLoop 可以被分配給多個 Channel(即一個線程管理多個連接)。
  • 一個 Channel 在它的生命周期內只注冊到一個 EventLoop(即一個連接只由一個線程管理)。這條黃金規則從根本上避免了 Channel 層面的并發問題,無需使用同步鎖。
  • 每個 EventLoop 都擁有自己的 SelectorTask Queue

1.2.4 底層實現原理

我們以最常用的 NioEventLoop 為例來分析其工作原理。

NioEventLoop 的工作循環:

等待事件
是否有事件?
處理 I/O 事件
執行任務隊列

EventLoop 是一個無限循環,不斷執行以下步驟:

  • 輪詢(select)I/O 事件(如 Socket 可讀、可寫)。

  • 處理就緒的 I/O 事件。

  • 執行任務隊列中的任務(如 channel.write() 提交的任務)。

  • 執行定時任務(Scheduled Tasks)。

? 這就是 Netty 高性能的根源:單線程處理多個 Channel,避免線程上下文切換開銷

  • NioEventLoop 的核心是 run() 方法,它是一個永不終止的循環,其工作內容可以精煉為三大步驟:
public final class NioEventLoop extends SingleThreadEventLoop {private final Selector selector;           // JDK NIO Selectorprivate final SelectStrategy selectStrategy; // 選擇策略private final Queue<Runnable> taskQueue;   // 普通任務隊列private final Queue<ScheduledFutureTask<?>> scheduledTaskQueue; // 定時任務隊列// 簡化后的代碼示例, 只包含核心功能protected void run() {for (;;) { // 無限循環try {// 1. 輪詢策略:檢查是否有就緒的I/O事件或任務int strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());switch (strategy) {case SelectStrategy.CONTINUE: ... case SelectStrategy.SELECT:// 如果沒有任務,就阻塞地等待I/O事件,超時時間為定時任務最早時間strategy = select(timeoutMillis); ... break;default:}// 2. 處理I/O事件:processSelectedKeys()// 3. 處理所有排隊的任務:runAllTasks()} catch (Throwable t) {... // 處理異常}}}
}

事件循環主流程

for (;;) {try {// 1. 輪詢 I/O 事件int selectedKeys = selector.select(timeoutMillis);// 2. 處理 I/O 事件if (selectedKeys != 0) {processSelectedKeys();}// 3. 執行任務隊列runAllTasks();// 4. 執行定時任務runAllTasks(timeoutMillis);} catch (Throwable t) {handleException(t);}
}

? 關鍵點

  • selector.select() 是阻塞調用,直到有事件就緒或超時。
  • runAllTasks() 執行用戶提交的任務(如 channel.write())。
  • 單線程串行執行,保證了 Channel 的線程安全。
  1. 輪詢I/O事件【Select】

    • 智能阻塞: Netty 會檢查任務隊列是否為空。如果隊列為空,它就會調用selector.select(timeout),進入阻塞狀態,等待 I/O 事件。

      final class DefaultSelectStrategy implements SelectStrategy {static final SelectStrategy INSTANCE = new DefaultSelectStrategy();private DefaultSelectStrategy() { }@Overridepublic int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {// 如果有任務, 則取出,如果沒有,則阻塞return hasTasks ? selectSupplier.get() : SelectStrategy.SELECT;}
      }
      
    • 這個 timeout 是基于定時任務隊列中最近要執行的任務來計算的,非常智能,既不會錯過 I/O 事件,也不會耽誤執行定時任務。

      private final IntSupplier selectNowSupplier = new IntSupplier() {@Overridepublic int get() throws Exception {return selectNow();}
      };
      
    • 如果隊列非空: 為了避免任務被延遲,它會調用 selector.selectNow(),這是一個非阻塞調用,立刻返回。

      • 優化: Netty 對 selectedKeys 集合進行了優化,使用數組替代了 JDK 的 HashSet,減少了迭代過程中的垃圾產生。
  2. 處理 I/O 事件 (processSelectedKeys)

  • 遍歷由 Selector 提供的就緒的 SelectionKey 集合。
  • 根據 key 的興趣事件(OP_ACCEPT, OP_READ, OP_WRITE等),調用對應的 ChannelPipeline 上的方法。
  • 例如,當有數據可讀時(OP_READ),會觸發 ChannelRead 事件,數據會從 Pipeline 的頭部開始流動,被一個個 ChannelInboundHandler 處理。
  1. 處理異步任務隊列 (runAllTasks)
  • 這是 Netty 保證線程安全和高性能的關鍵。所有用戶提交的異步任務(如 ctx.write()channelActive 事件觸發后的業務邏輯等)都會被放入這個任務隊列。
  • EventLoop 線程會一次性將隊列中的所有任務取出來并執行。
  • 這樣做的好處是:保證了所有對同一個 Channel 的操作都在同一個線程中順序執行,完全避免了多線程并發訪問的問題,無需同步

線程分配策略

  • 當一個新的 Channel 被創建并注冊到 EventLoopGroup 時,EventLoopGroup 會使用 next() 方法選擇一個 EventLoop 來管理它。默認策略是輪詢(round-robin),即均勻分配,以保證負載均衡。
  1. 任務隊列的實現

EventLoop 維護了兩個任務隊列:

  • 普通任務隊列:存放通過 execute ()、submit () 提交的任務
  • 定時任務隊列:存放通過 schedule () 等方法提交的定時任務
// 簡化的任務處理邏輯
protected boolean runAllTasks() {// 從定時任務隊列中移動已到期的任務到普通任務隊列fetchFromScheduledTaskQueue();// 獲取普通任務隊列中的任務Runnable task = pollTask();if (task == null) {return false;}// 處理所有任務for (;;) {try {// 執行任務task.run();} catch (Throwable t) {// 處理任務執行異常}// 獲取下一個任務task = pollTask();if (task == null) {// 所有任務處理完畢lastExecutionTime = ScheduledFutureTask.nanoTime();return true;}}
}// 從定時任務隊列中獲取已到期的任務
private void fetchFromScheduledTaskQueue() {long nanoTime = AbstractScheduledEventExecutor.nanoTime();Runnable scheduledTask = pollScheduledTask(nanoTime);while (scheduledTask != null) {// 將已到期的定時任務添加到普通任務隊列taskQueue.add(scheduledTask);scheduledTask = pollScheduledTask(nanoTime);}
}

1.2.5 最佳實踐與經驗總結

? 正確用法(Best Practices)

  1. 線程數設置
    • bossGroup:通常 1 個線程足夠。
    • WorkerGroup: 默認值是 CPU核心數 * 2。這是一個很好的經驗值。對于純 I/O 密集型應用(如代理、聊天服務器),可以適當調大。對于計算密集型業務,建議保持默認或甚至更少,并將耗時業務提交到獨立的業務線程池,避免阻塞 EventLoop 線程。
  2. EventLoop 復用
    • 一個 Channel 一旦注冊到 EventLoop終身綁定,不會遷移。
    • 所有 I/O 操作都在該 EventLoop 線程中執行,無需加鎖
  3. 避免阻塞 EventLoop
  • ? 不要在 ChannelHandler 中執行耗時操作(如數據庫查詢、文件讀寫)。
  • ? 應提交到業務線程池:
private final EventExecutorGroup businessGroup = new DefaultEventExecutorGroup(10);
pipeline.addLast(businessGroup, new BusinessHandler()); // 耗時操作放這里
  1. Channel 與 EventLoop 的綁定關系
  • 牢記 “一個 Channel 一生只由一個 EventLoop 處理” 的原則。利用這一點,可以在 ChannelHandler 中使用 ThreadLocal 變量而無需擔心線程安全問題。
  1. 優雅關閉
  • 必須調用 shutdownGracefully(),等待正在進行的任務完成。
  • 它會先平滑地關閉所有 Channel,然后不再接收新任務,最后處理完任務隊列中的任務后再終止線程。shutdownGracefully() 還支持設置安靜期和超時時間。
  1. 監控 EventLoop
  • 可通過 EventLooppendingTasks() 方法監控任務積壓情況。

EventLoopEventLoopGroup 是 Netty 的心臟和引擎。它們不僅僅是線程池,更是 Netty 實現高效、有序、線程安全的異步事件處理的核心機制。

  • EventLoopGroup 是工廠和調度器,負責分配 EventLoop
  • EventLoop 是勤勞的工作單元,一人一線程,負責其生命周期內所有的事件和任務。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/96320.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/96320.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/96320.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Cloud 高頻面試題詳解(含代碼示例與深度解析)

文章目錄Spring Cloud 高頻面試題詳解&#xff08;含代碼示例與深度解析&#xff09;1. 什么是 Spring Cloud&#xff1f;它與 Spring Boot 有什么關系&#xff1f;2. 服務發現&#xff1a;Eureka 和 Nacos 的區別與選型&#xff1f;Eureka 示例與原理Eureka vs Nacos 對比表3.…

Ascend310B重構驅動run包

在Atlas 200I AI加速模塊(Ascend310B)移植過程中如需要將自己編譯的Image、dt.img及內核模塊打包到啟動鏡像包中需要對"Ascend-hdk-310b-npu-driver-soc_<version>_linux-aarch64.run"(下面統稱驅動run包)進行重構。下面將介紹如何重構run包。 重構驅動run包需…

Leecode hot100 - 287. 尋找重復數

題目描述 287. 尋找重復數 - 力扣&#xff08;LeetCode&#xff09; 定一個包含 n 1 個整數的數組 nums &#xff0c;其數字都在 [1, n] 范圍內&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一個重復的整數。 假設 nums 只有 一個重復的整數 &#xff0c;返…

機器人控制器開發(驅動層——奧比大白相機適配)

文章總覽 編譯OrbbecSDK_ROS2的代碼 執行命令 colcon buildros2 launch orbbec_camera dabai.launch.py問題1&#xff1a; 運行時報錯&#xff1a; [component_container-1] [ERROR] [1757153916.450795107] [camera.camera_container]: Failed to load library: Could not…

`vcpkg` 微軟開源的 C/C++ 包管理工具的使用和安裝使用spdlog

vcpkg 是 微軟開源的 C/C 包管理工具&#xff0c;類似于 Python 的 pip、Node.js 的 npm、Rust 的 cargo。 它的主要作用是&#xff1a;幫助你快速下載、編譯、安裝和管理 C/C 第三方庫&#xff0c;并自動配置到你的項目&#xff08;比如 Visual Studio、CMake、MSBuild&#x…

Mysql 幻讀詳解

我們來詳細地聊一聊 MySQL InnoDB 中的“幻讀”&#xff08;Phantom Read&#xff09;問題。這是一個在數據庫事務隔離中非常核心且有時令人困惑的概念。 我會從定義、例子、原因以及解決方案幾個方面來徹底講清楚。 1. 什么是幻讀&#xff1f; 官方定義&#xff1a;幻讀指的…

如何生成 GitHub Token(用于 Hexo 部署):保姆級教程+避坑指南

如何生成 GitHub Token&#xff08;用于 Hexo 部署&#xff09;&#xff1a;保姆級教程避坑指南 前置說明&#xff1a;為什么需要 GitHub Token&#xff1f; 在使用 Hexo 部署博客到 GitHub Pages 時&#xff0c;你可能會遇到「密碼驗證失敗」或「需要雙重驗證」的問題——這…

常用加密算法之 AES 簡介及應用

相關系列文章 常用加密算法之 SM4 簡介及應用常用加密算法之 RSA 簡介及應用 引言 AES&#xff08;Advanced Encryption Standard&#xff0c;高級加密標準&#xff09;是一種??廣泛使用的對稱分組加密算法??&#xff0c;它使用相同的密鑰進行加密和解密操作&#xff0c…

Java面試問題記錄(一)

一、Java 核心基礎與進階1、我們知道 Java 中存在 “值傳遞” 和 “引用傳遞” 的說法&#xff0c;你能結合具體例子&#xff0c;說明 Java 到底是值傳遞還是引用傳遞嗎&#xff1f;這背后涉及到 JVM 中哪些內存區域的交互&#xff1f;Java中只有值傳遞&#xff0c;不存在引用傳…

Redis 主從復制、哨兵與 Cluster 集群部署

文章摘要 本文基于 VMware 虛擬機環境&#xff0c;詳細講解 Redis 高可用架構的核心組件與部署流程&#xff0c;涵蓋三大核心模塊&#xff1a;Redis 主從復制&#xff08;實現數據備份與讀寫分離&#xff09;、Redis 哨兵&#xff08;基于主從復制實現故障自動轉移&#xff0c;…

ElementUI 中 validateField 對部分表單字段數組進行校驗時多次回調問題

目錄 方案一&#xff1a;循環調用 Promise.all 合并結果 方案二&#xff1a;直接傳入數組字段 總結 在實際業務中&#xff0c;我們有時只需要對表單的部分字段進行校驗。ElementUI 提供的 validateField 方法支持單個字段&#xff0c;也支持字段數組&#xff0c;但在使用時…

Visual Studio 2026 震撼發布!AI 智能編程時代正式來臨

Visual Studio 2026 震撼發布&#xff01;AI 智能編程時代正式來臨 Visual Studio 2026 Insider圖標 開發者們的開發環境即將迎來前所未有的智能革命&#xff0c;微軟用Visual Studio 2026 重新定義了編碼體驗。 2025年9月10日&#xff0c;微軟正式推出了Visual Studio 2026 In…

Gamma AI:高效制作PPT的智能生成工具

你有沒有過這種崩潰時刻&#xff1f;領導讓你下午交一份產品介紹 PPT&#xff0c;你打開模板網站翻了半小時沒找到合適的&#xff0c;好不容易選了個模板&#xff0c;又得手動調整文字間距、搭配圖片&#xff0c;光是把數據做成圖表就花了一小時&#xff0c;最后趕出來的 PPT 還…

Python副業新玩法:用Flask搭小程序后端,躺賺被動收入的秘密

凌晨1點&#xff0c;林浩合上電腦時&#xff0c;手機彈出一條微信消息——是上周幫一家社區水果店搭的小程序后端&#xff0c;商家發來了當月的服務費到賬提醒。他靠在椅背上笑了&#xff1a;這是這個月第8筆“睡后收入”&#xff0c;加起來剛好覆蓋了下個月的房貸。半年前&…

基于PyQt5和阿里云TTS的語音合成應用開發實戰[附源碼】

項目概述 本文將詳細介紹一個基于PyQt5圖形界面框架和阿里云TTS(Text-to-Speech)服務的語音合成桌面應用程序的開發過程。該應用提供了完整的文字轉語音功能,包括多音色選擇、參數調節、實時試聽、語速調節和音頻下載等特性。 技術棧 前端界面: PyQt5 語音合成: 阿里云TTS服…

基于esp32c3 rust embassy 的墨水屏程序

EPD Reader 基于ESP32-C3的電子墨水屏閱讀器&#xff0c;支持ap 配網、sntp 時間同步、txt閱讀、天氣預報、顯示節假日信息、農歷顯示、自動休眠、web配置等功能。這是在另一個項目 一個rust embassy esp32c3 的練習項目-CSDN博客的基礎上修改的 。 界面比較粗糙&#xff0c;以…

Spring 單例測試及線程安全

創建一個賬戶類 package com.duanhw.demo22.account;import org.springframework.beans.factory.annotation.Value;//Service public class AccountService {Value("1000")private Integer balance;//存款public void deposit(Integer amount){int newbalance balanc…

【vue】組件寬度調整失效后,調整的方法

父容器布局限制 若組件放置在柵格布局&#xff08;如display: grid&#xff09;或彈性容器中&#xff0c;父元素的寬度限制可能導致子組件寬度失效。解決方案是為父容器設置明確的寬度&#xff0c;或通過百分比布局實現自適應16。例如&#xff1a; <div style"width:…

Java 在Word 文檔中插入頁眉頁腳:一份實用的編程指南

在現代企業應用中&#xff0c;Java 開發者經常需要處理各種文檔操作&#xff0c;其中對 Word 文檔的自動化處理尤為常見。無論是生成報告、合同還是其他商業文檔&#xff0c;頁眉頁腳作為文檔結構的重要組成部分&#xff0c;承載著公司 Logo、頁碼、版權信息等關鍵內容。手動添…

深入解析Dart虛擬機運行原理

Dart虛擬機運行原理 一、Dart虛擬機 1.1 引言 Dart VM是一種虛擬機&#xff0c;為高級編程語言Dart提供執行環境&#xff0c;但這并意味著Dart在D虛擬機上執行時&#xff0c;總是采用解釋執行或者JIT編譯。 例如還可以使用Dart虛擬機的AOT管道將Dart代碼編譯為機器代碼&#xf…