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/