Netty各版本之間的變化和需要注意的地方

jopen 9年前發布 | 63K 次閱讀 Netty 網絡工具包
 

本文翻譯自官方文檔 New and noteworthy in 5.0 .

本文帶你了解Netty 5.0 的值得注意的改變和新特性,幫助你將應用程序遷移到最新的版本.

不像 3.x 和 4.0 之間的改變那么大, 5.0 并沒有很大的改變,盡管它在設計的簡化性上做了一些突破. 我們盡可能平滑地使4.x轉換到5.0,但是如果在遷移的過程中有問題請讓我們知道.

其它版本之間的改變和新特性文檔請看:

Netty 4.1中的新變化和注意點

Netty 4.0中的新變化和注意點

核心改變

簡化 handler 類型繼承關系

ChannelInboundHandler和ChannelOutboundHandler被合并到 ChannelHandler . 現在ChannelHandler擁有inbound 和 outbound handler 方法.

ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter, 和ChannelDuplexHandlerAdapter棄用了, 被ChannelHandlerAdapter取代.

因為現在你無法區分一個 handler是 inbound handler 或者 outbound handler, 所以CombinedChannelDuplexHandler被ChannelHandlerAppender取代.

想了解這個改變的更多信息,請看 pull request #1999 .

channelRead0() → messageReceived()

我知道,這是一個 傻傻的錯誤 . 如果你正在使用SimpleChannelInboundHandler, 你不得不將channelRead0()重命名為messageReceived().

更靈活的線程模型

在Netty 4.x中, 每個EventLoop和一個固定的線程緊密耦合, 這個線程會執行它注冊的channel的所有的I/O事件,以及指派給它的任務.

在5.0中, 一個EventLoop不再直接使用線程,而是使用一個Executor. 也就是, 它使用一個Executor對象作為構造函數的參數,以前是在一個無盡的循環中拉取I/O事件, 吸納在是每次迭代的結果是一個task,將此task提交給Executor執行.

如果沒有特別指定,Executor默認使用ForkJoinPool.ForkJoinPool使用thread-local 隊列. 也就是說, 從線程A中提交到ForkJoinPool到非常可能再由線程A執行. 這提供了EventLoop高層次的thread affinity.

而且,程序員也可以提供他們自己的Executor(也叫做 thread pool) 調度EventLoop. 一個場景可以證明它有用:當 Netty用作大規模的軟件系統中. 假定此系統已經使用一個線程池并發地執行它的任務. Netty 4.x簡單的產生大量的線程,完全不顧它是一個大規模系統的一部分. 自Netty 5.0起, 開發者可以運行 Netty 和系統的其它部分在同一個線程池中, 通過應用更好的調度策略和較少的調度開支可以潛在地提高性能. 細節討論可以參照 GitHub issue 2250 .

應該提到的是,這個改變不會影響ChannelHandlers的方式. 在開發者看來,唯一改變的是不再保證同一個ChannelHandler會被同一個線程執行. 然而可以保證的是, 它不會同時被兩個或者兩個以上的線程執行.此外, Netty 會負責內存可見性的問題,所以不必擔心線程安全性和 ChannelHandler的volatile變量.

這個改變的另一個影響就是NioEventLoop,NioEventLoopGroup,EpollEventLoop和EpollEventLoopGroup不再使用ThreadFactory作為構造函數的參數. 這些構造函數現在取而代之使用Executor和ExecutorFactory對象.

更好的Channel.deregister(...)

Netty 4.0引入了Channel.deregister(...), 5.0中它的行為被更新了以便符合Netty的線程模型.

現在可以保證在ChannelHandler中提交到EventLoop中的所有task在Channel取消注冊(deregister)前都會被EventLoop執行

然而Channel.deregister(...)保留了非阻塞的操作,所以你不得不等待 返回的ChannelFuture成功后才能將它安全的注冊到另一個EventLoop.

當調用Channel.deregister(...)后任何嘗試在這個ChannelHandler中提交新的task (Runnable 或 Callable) 到會觸發RejectedExecutionException. 一旦這個Channel注冊到另外一個EventLoop, 一切歸于正常.

ChannelHandler通過EventLoop.schedule*(...)方法提交的task當Channel取消注冊后會停止執行, 當Channel再次注冊時這些task會自動移到新的EventLoop中繼續執行. 這個限制只會影響在Channel取消注冊時被調度的task. 那么delay的或者定期執行的task不受影響.

你也可以突破這個限制,盡管不被推薦. Netty 5.0 引入了一個新的方法EventLoop.unwrap(), 它返回原始的EventLoop并不會執行一個健全的檢查. 更準確的講, 當提交task或者調度task到 "unwrapped" EventLoop時, 不會保證這些task會被并發執行,調度的task也不保證自動移到新的EventLoop.

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!