跳過導覽

5.0 的新功能與注意事項

您知道這個頁面是從 Github Wiki 頁面 自動產生的嗎?您可以在這裡自行改善!

本文將引導您瀏覽 Netty 主要版本(自 4.1 起)的眾多顯著變更和新功能清單,讓您了解如何將應用程式移植到新版本。

與 3.x 與 4.0 之間的變更不同的是,儘管 5.0 在設計簡約性上產生了重大突破,但變動並不大。我們嘗試讓從 4.x 到 5.0 的過渡盡可能順暢,如果您在遷移期間遇到了任何問題,請務必告知我們。

核心變更

新的 Buffer API 取代 ByteBuf

Netty 5 引進了新的 Buffer API,比 ByteBuf 更簡單、更安全。詳細資訊請參閱 pull 要求 #11347。綜而言之,新的 API 有以下標題變更

  • 不再允許混疊。換句話說,您不能再有多個緩衝區參考同一記憶體。
    • 這意味著 sliceduplicate 及其保留變異已消失。
    • 引入了新的 API 作為替換:splitreadSplitsend。這些方法的合約可防止別名。
  • 參考計數實際上已消失。
    • retainrelease方法已消失。作為替代,緩衝區現在有一個close方法,此方法會在緩衝區壽命結束時呼叫。
    • 應設計 API 和整合,以確保緩衝區始終擁有唯一且明確的所有權。「借用」緩衝區應在 API 中減到最低並予以避免,因為參考計數不再可用於在執行階段追蹤借用或參照。
    • 大多數以前使用retain的地方實際上都只試圖取消超類別中無條件release的效果。在這些情況下,您可以使用split,因為您最有可能只想要傳遞緩衝區的可讀部分。
  • send方法和Send介面可用於編碼類型系統中的所有權轉移。
  • 緩衝區始終是大端,*LE方法已消失。
    • 若要執行小端讀取或寫入,請結合使用緩衝區上的大端讀取和寫入方法,在IntegerLong等上使用reverseBytes系列方法。
    • BufferUtil有一個方法用於反轉「中型」(3 個位元組) 整數。
  • 緩衝區實現具有更高的測試涵蓋率,並具有更一致的行為。

ChannelHandler

簡化的處理常式層級結構

ChannelInboundHandlerChannelOutboundHandler已合併到[ChannelHandler]。[ChannelHandler]現在同時擁有入站和出站處理常式方法。

ChannelInboundHandlerAdapterChannelOutboundHandlerAdapterChannelDuplexHandlerAdapter已移除,並由ChannelHandlerAdapter取代。

現在既然無法判斷處理常式是入站處理常式還是出站處理常式,因此CombinedChannelDuplexHandler已由ChannelHandlerAppender取代。

若要進一步瞭解此變更,請參閱pull 請求#1999

SimpleChannelInboundHandler.channelRead0()messageReceived()

如果您正在使用SimpleChannelInboundHandler,您必須將channelRead0()重新命名為messageReceived()

ChannelHandler方法簽章變更。

所有ChannelHandler出站方法(flushread除外)現在傳回Future<Void>。這會確保適當的傳播和鏈結。除此之外,我們還將exceptionCaught(...)重新命名為channelExceptionCaught(...),以清楚說明這會處理入站例外。

使用者事件

現在可以透過管道雙向觸發使用者/自訂事件。對於入站事件,您可以使用 fireChannelInboundEvent(...)(取代 fireUserEventTriggered(...)),而對於出站事件,則可以使用 sendOutboundEvent(...)。這兩個都可以用平常定義於 ChannelHandler 的方式攔截。

已新增 ChannelHandler.pendingOutboundBytes(...) 方法

現在可以輕易藉由 ChannelPipeline 中包含的 ChannelHandler,來影響 Channel 的可寫性。當 ChannelHandler 本身快取出站資料時,這使得可以影響反向壓力。

傳輸

半關閉

Netty5 已在其核心程式庫內建半關閉的支援。為此,已推出 ChannelHandler.shutdownChannelHandler.channelShutdown。除此之外,還新增了 Channel.isShutdown(...)ChannelOutboundInvoker.shutdown(...)。這將取代完全移除的舊 `DuplexChannel` 抽象化。有關更多詳情,請參閱 拉取請求 #12468

ChannelHandlerContext 不再延伸 AttributeMap

我們已變更 ChannelHandlerContext,使其不再延伸 AttributeMap。如果您使用屬性,您應該直接使用仍延伸 AttributeMapChannel

已移除 ChannelPipeline.add*(EventExecutorGroup...)

在 netty 4.x 中,我們新增了使用明確 EventExecutorGroupChannelHandler 加入 ChannelPipeline 的功能。雖然這看起來是個好主意,但事實證明,在生命週期方面會產生一些問題。

  • handlerRemoved(...)handlerAdded(...) 可能會在「錯誤的時間」被呼叫。這會造成一連串問題。最糟的情況是,當 handlerRemoved(...) 被呼叫,且處理常式釋出一些原生記憶體時,是因為預期處理常式永遠不會再被使用。在此之後,可能會呼叫 channelRead(...),然後嘗試存取先前釋出的記憶體,進而使 JVM 當機。
  • 正確實作管道的並發存取/修改方面的「可見性」也相當有問題。

考慮到這一點,我們發現使用者最想要的是讓來自的訊息由另一個執行緒處理,以處理商業邏輯。這最好透過由使用者提供的自訂實作來完成,因為使用者更能掌握何時可以銷毀某些項目。

Channel.eventLoop() 變更名稱為 Channel.executor()

在 netty 5.x 中,我們在 ChannelOutboundInvoker 新增 executor() 方法,而由於此方法會回傳 EventExecutor,因此我們決定從 Channel 中移除 eventLoop() 方法,並針對 Channel 覆寫 executor(),使其回傳 EventLoop

已新增 EventLoopGroup.isCompatible(...) 方法

現在可以在嘗試使用之前,檢查 Channel 子類型是否與 EventLoopGroup / EventLoop 相容。這有助於選擇正確的 Channel 子類型。

新增 EventLoop.registerForIo(...)EventLoop.deregisterForIo

新增下列方法到 EventLoop 介面,允許註冊和取消註冊 Channel。這些方法不應由使用者直接使用,而應由 Channel 實作本身使用。

移除 Channel.Unsafe

完全移除 Channel.Unsafe 介面,所以最終使用者無法搞亂內部結構。

ChannelOutboundBuffer 已不再是 Channel API 的一部分

ChannelOutboundBuffer 屬於我們的 AbstractChannel 實作的實作細節,因此已從 Channel 本身完全移除。

移除 Channel.beforeBeforeWritable(),並將 Channel.bytesBeforeUnwritable() 改名為 writableBytes()

我們移除 Channel.beforeBeforeWritable() 方法,因為它根本沒有被使用,並將 Channel.bytesBeforeUnwritable() 改名為 Channel.writableBytes

未來 / 承諾

移除 ProgressiveFuture / ProgressivePromiseChannelProgressiveFuture / ChannelProgressivePromise

netty 5 已完全移除對 ProgressiveFuture / ProgressivePromise 的支援。原因是雖然它有時可能有用,但它也要求處理程序管道中的所有處理常式在此採取特殊動作,如果它們鏈接承諾的話。事實上並非如此,而且在現實中做這件事相當麻煩。

由於上述問題,我們決定最好將此功能全部移除,因為原本就沒有太多人使用此功能。與其讓某些東西只「有時」運作,不如完全不支援某些東西。這也表示需要維護的程式碼更少。

移除 VoidChannelPromise

在 netty 4.1.x 中,可以使用 voidPromise() 方法取得特殊的 ChannelPromise 實作,可用於各種 IO 操作(例如 write),以減少建立的物件數量。雖然此功能的動機很好,但最後發現 ChannelPromise 的這個特殊範例確實帶來許多問題

  • 每個對此承諾新增 ChannelFutureListenerChannelHandler,都必須先呼叫 unvoid(),以確保可以安全地新增偵聽器。如果漏掉這個動作,一旦呼叫 addListener 就會導致 RuntimeException
  • 完全不支援 wait() / sync() 作業。
  • 某些作業允許使用 VoidChannelPromise,但有些則不允許。

變更 PromiseFuture 的 API

  • Future.addListeners()Future.removeListeners()Future.removeListener() 已刪除。我們移除了移除先前新增偵聽項的能力。此功能並未真正使用,所以允許我們移除部分的複雜性,並移除部分 API。
  • syncawait 方法的不中斷變體已移除。
  • 已新增 Future.isFailed() 方法,可檢查未來是否已完成且失敗。這與現有的 Future.isSuccess() 類似,用於檢查未來是否已完成且成功。
  • Promise.setUncancellable 現在僅在承諾從「未完成」轉變為「無法取消」時,才會傳回 true。此方法會傳回 false 如果承諾已完成,但在 4.1 中會傳回 true
  • 已新增新的 Future.map()Future.flatMap() 方法,可用於根據現有的未來輕鬆組合並建立新的未來。這些方法透過 lan=「zh-TW」propagation 來正確處理失敗和取消。
  • 所有封鎖方法都從 Future 介面移除,因為人們很容易誤用這些方法,進而封鎖 EventLoop。如果您仍需要從 EventLoop 外部封鎖,您需要透過 Future.asStage() 轉換 Future。傳回的 FutureCompletionStage 提供了封鎖方法。

Codec 變更

壓縮支援

我們的所有壓縮實作都已變更,以使用新的 壓縮 API,以便於在不同的編解碼器中重複使用,而無需建立額外的 EmbeddedChannel

HTTP 編解碼器

鮮少使用編解碼器已移至 Netty Contrib

為了精簡程式碼庫,並減輕維護負擔,下列編解碼器和處理程式已移至 Netty Contrib

  • netty-codec-xml
  • netty-codec-redis
  • netty-codec-memcache
  • netty-codec-stomp
  • netty-codec-haproxy
  • netty-codec-mqtt
  • netty-codec-socks
  • netty-handler-proxy
  • io.netty.handler.codec.json
  • io.netty.handler.codec.marshalling
  • io.netty.handler.codec.protobuf
  • io.netty.handler.codec.serialization
  • io.netty.handler.codec.xml
  • io.netty.handler.pcap

GraalVM 和原生映像

Netty 現在在執行時自動初始化,原生映像的額外大小開銷最少。最低支援的 Graal 版本現在為 22.1、Java 17。

最後取得時間:2024 年 19 月 7 日