5.0 的新功能與注意事項
本文將引導您瀏覽 Netty 主要版本(自 4.1 起)的眾多顯著變更和新功能清單,讓您了解如何將應用程式移植到新版本。
與 3.x 與 4.0 之間的變更不同的是,儘管 5.0 在設計簡約性上產生了重大突破,但變動並不大。我們嘗試讓從 4.x 到 5.0 的過渡盡可能順暢,如果您在遷移期間遇到了任何問題,請務必告知我們。
Netty 5 引進了新的 Buffer API,比 ByteBuf 更簡單、更安全。詳細資訊請參閱 pull 要求 #11347。綜而言之,新的 API 有以下標題變更
- 不再允許混疊。換句話說,您不能再有多個緩衝區參考同一記憶體。
- 這意味著
slice
、duplicate
及其保留變異已消失。 - 引入了新的 API 作為替換:
split
、readSplit
和send
。這些方法的合約可防止別名。
- 這意味著
- 參考計數實際上已消失。
retain
和release
方法已消失。作為替代,緩衝區現在有一個close
方法,此方法會在緩衝區壽命結束時呼叫。- 應設計 API 和整合,以確保緩衝區始終擁有唯一且明確的所有權。「借用」緩衝區應在 API 中減到最低並予以避免,因為參考計數不再可用於在執行階段追蹤借用或參照。
- 大多數以前使用
retain
的地方實際上都只試圖取消超類別中無條件release
的效果。在這些情況下,您可以使用split
,因為您最有可能只想要傳遞緩衝區的可讀部分。
send
方法和Send
介面可用於編碼類型系統中的所有權轉移。- 緩衝區始終是大端,
*LE
方法已消失。- 若要執行小端讀取或寫入,請結合使用緩衝區上的大端讀取和寫入方法,在
Integer
、Long
等上使用reverseBytes
系列方法。 -
BufferUtil
有一個方法用於反轉「中型」(3 個位元組) 整數。
- 若要執行小端讀取或寫入,請結合使用緩衝區上的大端讀取和寫入方法,在
- 緩衝區實現具有更高的測試涵蓋率,並具有更一致的行為。
ChannelInboundHandler
和ChannelOutboundHandler
已合併到[ChannelHandler
]。[ChannelHandler
]現在同時擁有入站和出站處理常式方法。
ChannelInboundHandlerAdapter
、ChannelOutboundHandlerAdapter
和ChannelDuplexHandlerAdapter
已移除,並由ChannelHandlerAdapter
取代。
現在既然無法判斷處理常式是入站處理常式還是出站處理常式,因此CombinedChannelDuplexHandler
已由ChannelHandlerAppender
取代。
若要進一步瞭解此變更,請參閱pull 請求#1999。
如果您正在使用SimpleChannelInboundHandler
,您必須將channelRead0()
重新命名為messageReceived()
。
所有ChannelHandler
出站方法(flush
和read
除外)現在傳回Future<Void>
。這會確保適當的傳播和鏈結。除此之外,我們還將exceptionCaught(...)
重新命名為channelExceptionCaught(...)
,以清楚說明這會處理入站例外。
現在可以透過管道雙向觸發使用者/自訂事件。對於入站事件,您可以使用 fireChannelInboundEvent(...)
(取代 fireUserEventTriggered(...)
),而對於出站事件,則可以使用 sendOutboundEvent(...)
。這兩個都可以用平常定義於 ChannelHandler
的方式攔截。
現在可以輕易藉由 ChannelPipeline
中包含的 ChannelHandler
,來影響 Channel
的可寫性。當 ChannelHandler
本身快取出站資料時,這使得可以影響反向壓力。
Netty5 已在其核心程式庫內建半關閉的支援。為此,已推出 ChannelHandler.shutdown
及 ChannelHandler.channelShutdown
。除此之外,還新增了 Channel.isShutdown(...)
及 ChannelOutboundInvoker.shutdown(...)
。這將取代完全移除的舊 `DuplexChannel` 抽象化。有關更多詳情,請參閱 拉取請求 #12468。
我們已變更 ChannelHandlerContext
,使其不再延伸 AttributeMap
。如果您使用屬性,您應該直接使用仍延伸 AttributeMap
的 Channel
。
在 netty 4.x 中,我們新增了使用明確 EventExecutorGroup
將 ChannelHandler
加入 ChannelPipeline
的功能。雖然這看起來是個好主意,但事實證明,在生命週期方面會產生一些問題。
-
handlerRemoved(...)
、handlerAdded(...)
可能會在「錯誤的時間」被呼叫。這會造成一連串問題。最糟的情況是,當handlerRemoved(...)
被呼叫,且處理常式釋出一些原生記憶體時,是因為預期處理常式永遠不會再被使用。在此之後,可能會呼叫channelRead(...)
,然後嘗試存取先前釋出的記憶體,進而使 JVM 當機。 - 正確實作管道的並發存取/修改方面的「可見性」也相當有問題。
考慮到這一點,我們發現使用者最想要的是讓來自的訊息由另一個執行緒處理,以處理商業邏輯。這最好透過由使用者提供的自訂實作來完成,因為使用者更能掌握何時可以銷毀某些項目。
在 netty 5.x 中,我們在 ChannelOutboundInvoker
新增 executor()
方法,而由於此方法會回傳 EventExecutor
,因此我們決定從 Channel
中移除 eventLoop()
方法,並針對 Channel
覆寫 executor()
,使其回傳 EventLoop
。
現在可以在嘗試使用之前,檢查 Channel
子類型是否與 EventLoopGroup
/ EventLoop
相容。這有助於選擇正確的 Channel
子類型。
新增下列方法到 EventLoop
介面,允許註冊和取消註冊 Channel
。這些方法不應由使用者直接使用,而應由 Channel
實作本身使用。
完全移除 Channel.Unsafe
介面,所以最終使用者無法搞亂內部結構。
ChannelOutboundBuffer
屬於我們的 AbstractChannel
實作的實作細節,因此已從 Channel
本身完全移除。
我們移除 Channel.beforeBeforeWritable()
方法,因為它根本沒有被使用,並將 Channel.bytesBeforeUnwritable()
改名為 Channel.writableBytes
。
netty 5 已完全移除對 ProgressiveFuture / ProgressivePromise 的支援。原因是雖然它有時可能有用,但它也要求處理程序管道中的所有處理常式在此採取特殊動作,如果它們鏈接承諾的話。事實上並非如此,而且在現實中做這件事相當麻煩。
由於上述問題,我們決定最好將此功能全部移除,因為原本就沒有太多人使用此功能。與其讓某些東西只「有時」運作,不如完全不支援某些東西。這也表示需要維護的程式碼更少。
在 netty 4.1.x 中,可以使用 voidPromise()
方法取得特殊的 ChannelPromise
實作,可用於各種 IO 操作(例如 write
),以減少建立的物件數量。雖然此功能的動機很好,但最後發現 ChannelPromise
的這個特殊範例確實帶來許多問題
- 每個對此承諾新增
ChannelFutureListener
的ChannelHandler
,都必須先呼叫unvoid()
,以確保可以安全地新增偵聽器。如果漏掉這個動作,一旦呼叫addListener
就會導致RuntimeException
。 - 完全不支援 wait() / sync() 作業。
- 某些作業允許使用
VoidChannelPromise
,但有些則不允許。
Future.addListeners()
、Future.removeListeners()
和Future.removeListener()
已刪除。我們移除了移除先前新增偵聽項的能力。此功能並未真正使用,所以允許我們移除部分的複雜性,並移除部分 API。sync
和await
方法的不中斷變體已移除。- 已新增
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
提供了封鎖方法。
我們的所有壓縮實作都已變更,以使用新的 壓縮 API,以便於在不同的編解碼器中重複使用,而無需建立額外的 EmbeddedChannel
。
- 已移除 netty-core 對多部分的支持。這將在未來移轉至 netty 5 作為貢獻者儲存庫。(https://github.com/netty/netty/pull/11830)
- 已移除較舊的 WebSocket 草稿 (https://github.com/netty/netty/pull/11831)
- HTTP/2 標頭驗證現在已預設啟用。這將導致格式錯誤的請求在預設情況下會被拒絕。
為了精簡程式碼庫,並減輕維護負擔,下列編解碼器和處理程式已移至 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
Netty 現在在執行時自動初始化,原生映像的額外大小開銷最少。最低支援的 Graal 版本現在為 22.1、Java 17。