Netty 5.0 中的新變化和注意點
本文翻譯自官方文檔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.
來自:http://colobu.com/2015/08/18/netty-new-and-noteworthy-in-5-0/