跳過導覽

Netty 5 遷移指南

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

Netty 5 遷移指南

套件

為了讓 Netty 5 和 Netty 4 能夠共存於同一個 classpath 上,我們已將 Netty 5 類別的套件名稱變更為 io.netty5.*

緩衝區

Netty 5 導入了新的緩衝區 API,比 ByteBuf 更簡單且更安全的使用。

容量

Netty 4.1 ByteBuf 會在編寫時自動擴充容量直至達到最大容量。

新的 Buffer API 不再執行此動作,也不再區分容量和最大容量。程式碼應改為分配適當大小的緩衝區(大小參數現在為強制性),和/或視需要呼叫 ensureWritable()ensureWritable() 方法現在也可以接受允許壓縮(與舊有的 discardReadBytes() 相同)和擴充的參數,一次執行單一記憶體複製。

轉接器

包含了轉換兩個 API 的轉接器,並允許它們共存,直至所有 handles 和相關程式碼已遷移為止。

ByteBufBuffer.wrap() 方法採用 ByteBuf 實例並回傳 Buffer,也就是新 API 的緩衝區實例。相反地,ByteBufAdaptor.intoByteBuf 方法採用 Buffer 並回傳 ByteBuf。這兩個方法會以最有效率的方式進行轉換,且多次轉換會互相抵消以避免巢狀轉接器。

BufferConversionHandler 中,它也可以插入仰賴不同 API 來處理的管線當中。請注意,BufferConversionHandler 無法轉換任何 ByteBufHolderBufferHolder 物件。包含 BufferByteBuf 實例的物件將需要使用自訂 MessageToMessageCodec 來轉換。

API 的變更

Buffer API 中的主要差異在於不再允許別名。別名是在兩個或兩個以上的緩衝區物件參照相同的底層記憶體時產生。這表示方法(例如 slice()duplicate())不再可用。另一方面,生命週期處理已簡化,且參照計數可以完全從 API 中移除。

只要您使用 slice()duplicate()及其 retain* 變體,以及 retain() 方法系列,您現在改用 split()readSplit()copy()

split 方法系列與 slice() 類似,但它們只能在緩衝區的尾端進行切片,且回傳的緩衝區切片會從原始緩衝區中移除,因此可以避免別名。

retain()release() 方法已經消失。緩衝區改用 close() 方法,緩衝區的生命週期的結尾會呼叫它。緩衝區會實作 AutoCloseable 作為一種方便功能,只要緩衝區的範圍和生命週期完全在本地端。

在多數使用 retain() 的地方,實際上是為了取消超級類別或子類別中無條件 release() 的效用。在這種情況下,可以使用 split(),因為這些情況通常涉及傳遞緩衝區的可讀區段,而 split() 會產生平行必須關閉的兩個緩衝區。

有一個新的 send() 方法,可用於在類型系統中編碼,表示緩衝區的「所有權」從一個地方移動到另一個地方。例如,CompositeBuffer 工廠方法用它來確保複合緩衝區取得對元件緩衝區的獨佔權限。這可防止透過緩衝區組合產生別名。

緩衝區現在總是為大端序,且 *LE 取用器方法已消失。若要執行小端序讀取或寫入作業,請使用 IntegerLong 中的 reverseBytes 方法,搭配大端序讀取或寫入。

未來/承諾

移除 ChannelFuture/ChannelPromise

為簡化 API 和類型層級,我們決定完全移除 ChannelFuture / ChannelPromise(及其所有子類型 / 實作)。作為替代,可以直接使用 Future<Void>Promise<Void>

移除 ProgressiveFuture / ProgressivePromise 和 ChannelProgressiveFuture / ChannelProgressivePromise

ProgressiveFuture / ProgressivePromise 的支援已在 netty 5 中移除。這樣做的原因是,儘管它可能有時候會派上用場,但它也需要處理管線中所有的處理常式,如果這些處理常式連結承諾的話。這並非事實,而且實際上很麻煩。

基於上述問題,我們決定只移除這項功能,因為這項功能的使用率並不高。與其提供只能「偶爾」運作的功能,不如不提供支援。這也表示維護的程式碼會減少。

移除 VoidChannelPromise

在 netty 4.1.x 中,可以使用 voidPromise() 方法取得特殊的 ChannelPromise 實作,此方法可搭配各種 IO 作業(例如 write)來減少建立的物件數目。儘管這項功能的動機很好,但事實上,這個特殊的 ChannelPromise 實作的確帶來了許多問題

Promise 和 Future 的 API 變更

  • 已移除 Future.addListeners()Future.removeListeners()Future.removeListener()。我們已移除移除先前已新增監聽者的功能。這項功能並沒有被實際使用,因此讓我們得以移除部分複雜度並簡化部分 API 表面。
  • 已新增 Future.isFailed() 方法,此方法會檢查未來是否已完成且失敗。這類似現有的 Future.isSuccess(),後者會檢查未來是否已完成且成功。
  • 已新增 Future.map()Future.flatMap() 方法,此方法可輕鬆根據現有未來組合並建立新的未來。這些方法會透過傳遞適當地處理失敗及取消。
  • 已新增轉換為 CompletionStage 的新方法,如此一來能夠更容易與其他 API 互動。
  • 已移除 Future 介面中所有的封鎖方法,因為人們可能會誤用這些方法,進而封鎖 EventLoop。如果您仍需要從 EventLoop 外部封鎖,則需要透過 Future.asStage() 轉換 Future。回傳的 FutureCompletionStage 會提供封鎖方法。

Channel

Channel.eventLoop() 改名為 Channel.executor()

在 netty 5.x 中,我們將 executor() 方法新增到 ChannelOutboundInvoker,由於此方法會回傳 EventExecutor,我們決定從 Channel 移除 eventLoop() 方法,並僅覆寫 executor(),使它為 Channel 回傳 EventLoop

傳回型別已從 ChannelFuture 變更為 Future<Void>

由於我們已移除 ChannelFuture / ChannelPromise,因此也將方法的傳回型別變更為 Future<Void>

半關閉

Netty5 已在其核心支援半關閉。因此引入了 ChannelHandler.shutdownChannelHandler.channelShutdown。此外,還新增了 Channel.isShutdown(...)ChannelOutboundInvoker.shutdown(...)。這取代了舊有的 DuplexChannel 抽象,該抽象已被完全移除。如需更多詳細資訊,請參閱 プルリクエスト #12468

ChannelHandlerContext 不再延伸 AttributeMap

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

移除 Channel.Unsafe

Channel.Unsafe 介面已被完全移除,因此終端使用者無法干擾內部系統。

ChannelOutboundBuffer 不再是 Channel API 的一部分

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

移除 Channel.beforeBeforeWritable(),並將 Channel.bytesBeforeUnwritable() 變更名稱為 writableBytes()

我們已移除 Channel.beforeBeforeWritable() 方法,因為它根本沒有使用過,並將 Channel.bytesBeforeUnwritable() 變更名稱為 Channel.writableBytes

ChannelPipeline

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

在 netty 4.x 中,我們新增了使用明確的 EventExecutorGroupChannelHandler 新增至 ChannelPipeline 的功能。儘管這看似是一個好主意,但結果發現,在進入生命週期時會產生一些問題

  • handlerRemoved(...)handlerAdded(...) 可能在「錯誤的時間」被呼叫。這可能會造成許多問題。最糟糕的情況可能是呼叫 handlerRemoved(...),而處理常式會解放一些原生記憶體,因為它預期以後都不會再使用該處理常式。然後,在呼叫此方法後,可能會呼叫 channelRead(...),而 channelRead(...) 會嘗試存取先前解放的記憶體,並因此導致 JVM 崩潰。
  • 在執行緒存取/修改管線的情況下,正確實作「可見性」也很有問題。

考量到這一點,我們發現使用者實際上最想要的是,讓另一個執行緒處理接收到的訊息,以處理商業邏輯。這最好在由使用者提供的自訂實作中完成,因為使用者可以更妥善處理可以或不可以刪除的時機。

傳回型別已從 ChannelFuture 變更為 Future<Void>

由於我們已移除 ChannelFuture / ChannelPromise,因此也將方法的傳回型別變更為 Future<Void>

ChannelHandler

Netty 5 大幅簡化了 ChannelHandler 的類型階層。

簡化的處理常式類型階層

ChannelInboundHandlerChannelOutboundHandler 已被整合至 [ChannelHandler]。[ChannelHandler] 現在同時具備 inbound 和 outbound 處理程序方法。所有帶有 ChannelPromise 的 outbound 方法都已變更為回傳 Future<Void>。此變更有助於減少錯誤,並簡化 API。

ChannelInboundHandlerAdapterChannelOutboundHandlerAdapterChannelDuplexHandlerAdapter 已移除,並由 [ChannelHandlerAdapter] 取代。

由於現在無法判斷處理程序是 inbound 還是 outbound 處理程序,因此 CombinedChannelDuplexHandler 已由 [ChannelHandlerAppender] 取代。

有關此變更的詳細資訊,請參閱 協力要求 #1999

channelRead0()messageReceived()

如果您使用 [SimpleChannelInboundHandler],您需要將 channelRead0() 重新命名為 messageReceived()

使用者事件

現在可以使用 fireChannelInboundEvent(...) (取代至 fireUserEventTriggered(...)) 和 sendOutboundEvent(...) 在管道中以上下游方向發送使用者/自訂事件。這兩種方式皆可透過定義在 ChannelHandler 中的方法攔截,就像以往一樣。

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

現在可以透過 ChannelPipeline 中的 ChannelHandler 輕鬆地影響 Channel 的可寫性。藉由這樣的變更,只要 ChannelHandler 本身會緩衝 outbound 資料,就能夠影響反壓。

EventLoopGroup/EventLoop

雖然在 netty 4.x 中,我們使用不同的 EventLoopGroup/ EventLoop 實作來應付各式不同的傳輸 (例如 NioEventLoopGroupEpollEventLoopGroup ),我們已在 Netty 5 中變更為只使用一個叫做 MultiThreadEventLoopGroupEventLoopGroup 實作。這個 MultiThreadEventLoopGroup 會使用特定於傳輸本身的 IoHandlerFactory (例如 NioHandler.newFactory()EpollHandler.newFactory())。這樣的變更帶來許多優點。舉例來說,很容易擴充 MultiThreadEventLoopGroup,並為其加入裝飾或新增自訂指標等等。這樣一來,這個實作就可以在不同的傳輸類型中重複使用。這與 JDK 中提供、具有自訂化可能性的 ThreadPoolExecutor 非常類似。

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

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

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

已新增方法至 EventLoop 介面,以允許註冊和取消註冊 Channel。使用者不應自行使用這些方法,但可由 Channel 實作本身使用。

已將極少使用的編解碼器移至 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
最後檢索於 19-Jul-2024