Netty 5.0 中的新變化和注意點

jopen 9年前發布 | 13K 次閱讀 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 類型繼承關系

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

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

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

想了解這個改變的更多信息,請看 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, EpollEventLoopEpollEventLoopGroup 不再使用ThreadFactory作為構造函數的參數. 這些構造函數現在取而代之使用ExecutorExecutorFactory對象.

更好的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.


來自:http://colobu.com/2015/08/18/netty-new-and-noteworthy-in-5-0/

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