1. 程式人生 > >Netty(RPC高性能之道)原理剖析

Netty(RPC高性能之道)原理剖析

one int 定位 很多 pre sage fire 但是 iobuffer

1,Netty簡述

Netty 是一個基於 JAVA NIO 類庫的異步通信框架,用於創建異步非阻塞、基於事件驅動、高性能、高可靠性和高可定制性的網絡客戶端和服務器端
RPC高性能分析,請參考文章“【總結】RPC性能之道 ”
特點
    異步、非阻塞、基於事件驅動的NIO框架
    支持多種傳輸層通信協議,包括TCP、UDP等
    開發異步HTTP服務端和客戶端應用程序
    提供對多種應用層協議的支持,包括TCP私有協議、HTTP協議、WebSocket協議、文件傳輸等
    默認提供多種編解碼能力,包括Java序列化、Google的ProtoBuf、二進制編解碼、Jboss marshalling、文本字符串、base64、簡單XML等,這些編解碼框架可以被用戶直接使用
    提供形式多樣的編解碼基礎類庫,可以非常方便的實現私有協議棧編解碼框架的二次定制和開發
    經典的ChannelFuture-listener機制,所有的異步IO操作都可以設置listener進行監聽和獲取操作結果
    基於ChannelPipeline-ChannelHandler的責任鏈模式,可以方便的自定義業務攔截器用於業務邏輯定制
    安全性:支持SSL、HTTPS
    可靠性:流量整形、讀寫超時控制機制、緩沖區最大容量限制、資源的優雅釋放等
    簡潔的API和啟動輔助類,簡化開發難度,減少代碼量
為什麽不是傳統的IO
    線程模型存在致命缺陷:一連接一線程的模型導致服務端無法承受大量客戶端的並發連接
    性能差:頻繁的線程上下文切換導致 CPU 利用效率不高
    可靠性差:由於所有的 IO 操作都是同步的,所以業務線程只要進行 IO 操作,也會存在被同步阻塞的風險,這會導致系統的可靠性差,依賴外部組件的處理能力和網絡的情況
使用NIO,同步阻塞 IO 的三個缺陷都將迎刃而解
    NIO 采用 Reactor 模式,一個 Reactor 線程聚合一個多路復用器 Selector,它可以同時註冊、監聽和輪詢成百上千個 Channel,一個 IO 線程可以同時並發處理N個客戶端連接,線程模型優化為1:N(N < 進程可用的最大句柄數)或者 M : N (M通常為 CPU 核數 + 1, N < 進程可用的最大句柄數)
    由於 IO 線程總數有限,不會存在頻繁的 IO 線程之間上下文切換和競爭,CPU 利用率高
    所有的 IO 操作都是異步的,即使業務線程直接進行 IO 操作,也不會被同步阻塞,系統不再依賴外部的網絡環境和外部應用程序的處理性能
    切換到 NIO 編程之後可以為系統帶來巨大的可靠性、性能提升,所以,目前采用 NIO 進行通信已經逐漸成為主流
為什麽不直接使用JDK NIO類庫
    NIO的類庫和API繁雜,使用麻煩,你需要熟練掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等
    需要具備其它的額外技能做鋪墊,例如熟悉Java多線程編程,因為NIO編程涉及到Reactor模式,你必須對多線程和網路編程非常熟悉,才能編寫出高質量的NIO程序
    可靠性能力補齊,工作量和難度都非常大。例如客戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等等,NIO編程的特點是功能開發相對容易,但是可靠性能力補齊工作量和難度都非常大
    JDK NIO的BUG,例如臭名昭著的epoll bug,它會導致Selector空輪詢,最終導致CPU 100%。官方聲稱在JDK1.6版本的update18修復了該問題,但是直到JDK1.7版本該問題仍舊存在,只不過該bug發生概率降低了一些而已,它並沒有被根本解決
    一個高性能、高可靠性的 NIO 服務端開發和維護成本都是非常高的,開發者需要具有豐富的 NIO 編程經驗和網絡維護經驗,很多時候甚至需要通過抓包來定位問題
    開發出一套 NIO 程序需要 1 個月,但是它的穩定很可能需要 1 年甚至更長的時間
    Netty 的健壯性、功能、性能、可定制性和可擴展性在同類框架中都是首屈一指。



2,Netty原理

Netty邏輯架構
    第一層
        Reactor 通信調度層,它由一系列輔助類組成,包括 Reactor 線程NioEventLoop 以及其父類、NioSocketChannel/NioServerSocketChannel 以及其父類、ByteBuffer 以及由其衍生出來的各種 Buffer、Unsafe 以及其衍生出的各種內部子類等
    第二層
        職責鏈 ChannelPipeLine,它負責調度事件在職責鏈中的傳播,支持動態的編排職責鏈,職責鏈可以選擇性的攔截自己關心的事件,對於其它IO操作和事件忽略,Handler同時支持inbound和outbound事件
    第三層
        業務邏輯編排層,業務邏輯編排層通常有兩類:一類是純粹的業務邏輯編排,還有一類是應用層協議插件,用於協議相關的編解碼和鏈路管理,例如 CMPP 協議插件
靈拷貝
    “零拷貝”是指計算機操作的過程中,CPU不需要為數據在內存之間的拷貝消耗資源。而它通常是指計算機在網絡上發送文件時,不需要將文件內容拷貝到用戶空間(User Space)而直接在內核空間(Kernel Space)中傳輸到網絡的方式
    Netty的“零拷貝”主要體現在三個方面
        Netty的接收和發送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行字節緩沖區的二次拷貝。如果使用傳統的堆內存(HEAP BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然後才寫入Socket中。相比於堆外直接內存,消息在發送過程中多了一次緩沖區的內存拷貝
            讀取直接從“堆外直接內存”,不像傳統的堆內存和直接內存拷貝
            ByteBufAllocator 通過ioBuffer分配堆外內存
        Netty提供了組合Buffer對象,可以聚合多個ByteBuffer對象,用戶可以像操作一個Buffer那樣方便的對組合Buffer進行操作,避免了傳統通過內存拷貝的方式將幾個小Buffer合並成一個大的Buffer
            Netty允許我們將多段數據合並為一整段虛擬數據供用戶使用,而過程中不需要對數據進行拷貝操作
            組合Buffer對象,避免了內存拷
            ChannelBuffer接口:Netty為需要傳輸的數據制定了統一的ChannelBuffer接口
                    使用getByte(int index)方法來實現隨機訪問
                    使用雙指針的方式實現順序訪問
                    Netty主要實現了HeapChannelBuffer,ByteBufferBackedChannelBuffer, 與Zero Copy直接相關的CompositeChannelBuffer類
            CompositeChannelBuffer類
                CompositeChannelBuffer類的作用是將多個ChannelBuffer組成一個虛擬的ChannelBuffer來進行操作
                為什麽說是虛擬的呢,因為CompositeChannelBuffer並沒有將多個ChannelBuffer真正的組合起來,而只是保存了他們的引用,這樣就避免了數據的拷貝,實現了Zero Copy,內部實現
                    其中readerIndex既讀指針和writerIndex既寫指針是從AbstractChannelBuffer繼承而來的
                    components是一個ChannelBuffer的數組,他保存了組成這個虛擬Buffer的所有子Buffer
                    indices是一個int類型的數組,它保存的是各個Buffer的索引值
                    lastAccessedComponentId是一個int值,它記錄了最後一次訪問時的子Buffer ID
                CompositeChannelBuffer實際上就是將一系列的Buffer通過數組保存起來,然後實現了ChannelBuffer 的接口,使得在上層看來,操作這些Buffer就像是操作一個單獨的Buffer一樣
        Netty的文件傳輸采用了transferTo方法,它可以直接將文件緩沖區的數據發送到目標Channel,避免了傳統通過循環write方式導致的內存拷貝問題
            Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實現了零拷貝的功能,而在Netty中也通過在FileRegion中包裝了NIO的FileChannel.transferTo()方法實現了零拷貝
內存池:
    堆外直接內存的分配和回收是一個非常耗時的操作
    通過內存池對緩存區的復用
    Netty提供了多種內存管理策略,通過在啟動輔助類中配置相關參數,可以實現差異化的定制
    采用內存池的ByteBuf相比於朝生夕滅的ByteBuf,性能高23倍左右
    Netty提供四種ByteBuf
        基於內存池可重復利用的非堆內存:PooledDirectByteBuf
        基於內存池可重復利用的堆內存:PooledHeapByteBuf
        朝生夕滅的非堆內存:UnpooledDirectByteBuf
        朝生夕滅的堆內存:UnpooledHeapByteBuf
    為了更高效的管理內存,做到自動/及時的釋放不再引用的對象,Netty內置的資源對象實現ReferenceCounted接口,對內存的申請和釋放做統一管理
Reactor線程模型
    Reactor單線程模型
        一個線程中,Acceptor進行請求派發,處理連接請求,驗證, 通過Dispatch將對應的ByteBuffer派發到指定的Handler上進行消息解碼,進行業務邏輯Handler工作
        小容量應用場景,可以使用單線程模型,對於高負載、大並發的應用卻不合適
            一個NIO線程同時處理成百上千的鏈路,性能上無法支撐,即便NIO線程的CPU負荷達到100%,也無法滿足海量消息的編碼、解碼、讀取和發送
            當NIO線程負載過重之後,處理速度將變慢,這會導致大量客戶端連接超時,超時之後往往會進行重發,這更加重了NIO線程的負載,最終會導致大量消息積壓和處理超時,NIO線程會成為系統的性能瓶頸
            可靠性問題:一旦NIO線程意外跑飛,或者進入死循環,會導致整個系統通信模塊不可用,不能接收和處理外部消息,造成節點故障
    Reactor多線程模型
        一個專門NIO Acceptor線程監聽服務端,接受客戶端TCP連接請求
        由一個NIO線程池( 可以采用標準的JDK線程池實現,它包含一個任務隊列和N個可用的線程)進行IO操作,讀寫,編碼,消息發送接受
        1個NIO線程可以同時處理N條鏈路,但是1個鏈路只對應1個NIO線程,防止發生並發操作問題
        絕大多數場景下,Reactor多線程模型都可以滿足性能需求, 在極特殊應用場景中,一個NIO線程負責監聽和處理所有的客戶端連接可能會存在性能問題
            例如百萬客戶端並發連接,或者服務端需要對客戶端的握手消息進行安全認證,認證本身非常損耗性能
    主從Reactor多線程模型
        服務端用於接收客戶端連接的不再是個1個單獨的NIO線程,而是一個獨立的NIO線程池
        Acceptor接收到客戶端TCP連接請求處理完成後(可能包含接入認證等),將新創建的SocketChannel註冊到IO線程池(sub reactor線程池)的某個IO線程上,由它負責SocketChannel的讀寫和編解碼工作
        Acceptor線程池僅僅只用於客戶端的登陸、握手和安全認證,一旦鏈路建立成功,就將鏈路註冊到後端subReactor線程池的IO線程上,由IO線程負責後續的IO操作
        子線程池進行消息接受,發送,編碼等業務處理
    Netty的線程模型可以通過創建不同的EventLoopGroup實例並通過適當的參數配置,就可以支持上述三種Reactor線程模型
    Netty 對Reactor線程模型的支持提供了靈活的定制能力,可以滿足不同業務場景的性能訴求
無鎖化的串行設計
    大多數場景下,並行多線程處理可以提升系統的並發性能。但是,如果對於共享資源的並發訪問處理不當,會帶來嚴重的鎖競爭,這最終會導致性能的下降
    為了盡可能避免鎖競爭來的性能損耗,盡量讓消息處理放在同一個線程內完成,避免頻繁線程切換,避免多線程競爭和鎖
    Netty采用了串行無鎖化設計,在IO線程內部進行串行操作,避免多線程競爭導致的性能下降。表面上看,串行化設計似乎CPU利用率不高,並發程度不夠。但是,通過調整NIO線程池的線程參數,可以同時啟動多個串行化的線程並行運行,這種局部無鎖化的串行線程設計相比一個隊列-多個工作線程模型性能更優
    Netty的NioEventLoop讀取到消息之後,直接調用ChannelPipeline的fireChannelRead(Object msg),只要用戶不主動切換線程,一直會由NioEventLoop調用到用戶的Handler,期間不進行線程切換,這種串行化處理方式避免了多線程操作導致的鎖的競爭,從性能角度看是最優的
高效的並發編程
    對共享的可變數據進行正確的同步
        ServerBootstrap的ServerSocketChannel的Socket屬性是非線程安全的LinkedHashMap所以如果多線程創建、訪問和修改LinkedHashMap 時,必須在外部進行必要的同步
        考慮到鎖的範圍需要盡可能的小,我們對傳參的option 和value 的合法性判斷不需要加鎖。因此,代碼才對兩個判斷分支獨立加鎖,保證鎖的範圍盡可能的細粒度
    正確的使用鎖
        wait 方法別用來使線程等待某個條件,它必須在同步塊內部被調用,這個同步塊通常會鎖定當前對象實例
        始終使用wait 循環來調用wait 方法,永遠不要在循環之外調用wait 方法。原因是盡管條件並不滿足被喚醒條件, 但是由於其它線程意外調用notifyAll()方法會導致被阻塞線程意外喚醒,此時執行條件並不滿足,它將破壞被鎖保護的約定關系,導致約束失效,引起意想不到的結果
        喚醒線程,應該使用notify 還是notifyAll,當你不知道究竟該調用哪個方法時,保守的做法是調用notifyAll 喚醒所有等待的線程。從優化的角度看,如果處於等待的所有線程都在等待同一個條件,而每次只有一個線程可以從這個條件中被喚醒,那麽就應該選擇調用notify
    Volatile大量正確使用
        線程可見性:當一個線程修改了被volatile 修飾的變量後,無論是否加鎖,其它線程都可以立即看到最新的修改,而普通變量卻做不到這點
        禁止指令重排序優化,普通的變量僅僅保證在該方法的執行過程中所有依賴賦值結果的地方都能獲取正確的結果,而不能保證變量賦值操作的順序與程序代碼的執行順序一致
        volatile 僅僅解決了可見性的問題,但是它並不能保證互斥性,也就是說多個線程並發修改某個變量時,依舊會產生多線程問題。因此,不能靠volatile 來完全替代傳的鎖
    CAS和原子類廣泛使用
        互斥同步最主要的問題就是進行線程阻塞和喚醒所帶來的性能問題,因此這種同步被稱為阻塞同步,它屬於一種悲觀的並發策略,我們稱之為悲觀鎖
        隨著硬件和操作系統指令集的發展和優化,產生了非阻塞同步,被稱為樂觀鎖。簡單的說就是先進行操作,操作完成之後再判斷下看看操作是否成功,是否有並發問題,如果有進行失敗補償,如果沒有就算操作成功,這樣就從根本上避免了同步鎖的弊端
        JAVA 自帶的Atomic 原子類,可以避免同步鎖帶來的並發訪問性能降低的問題
        Netty 中對於int、long、boolean 等大量使用其原子類,減少了鎖的應用,降低了頻繁使用同步鎖帶來的性能下降
    線程安全類的應用
        java.util.concurrent包中提供了一系列的線程安全集合、容器和線程池,利用這些新的線程安全類可以極大的降低Java 多線程編程的難度,提升開發效率
            線程池Executor Framework以及定時任務相關的類庫,包括Timer等
            並發集合,包括List、Queue、Map和Set等
            新的同步器,例如讀寫鎖ReadWriteLock等
            新的原子包裝類,例如AtomicInteger
        在實際編碼過程中,我們建議通過使用線程池、Task(Runnable/Callable)、原子類和線程安全容器來代替傳統的同步鎖、wait 和notify,提升並發訪問的性能、降低多線程編程的難度
        JDK 的線程安全容器底層采用了CAS、volatile 和ReadWriteLock 實現,相比於傳統重量級的同步鎖,采用了更輕量、細粒度的鎖,因此,性能會更高。采用這些線程安全容器,不僅僅能提升多線程並發訪問的性能,還能降低開發難度
    通過讀寫鎖提升並發性能
        JDK1.5 新的並發編程工具包中新增了讀寫鎖,它是個輕量級、細粒度的鎖,合理的使用讀寫鎖,相比於傳統的同步鎖,可以提升並發訪問的性能和吞吐量,在讀多寫少的場景下,使用同步鎖比同步塊性能高一大截
        讀寫鎖的使用總結
            主要用於讀多寫少的場景,用來替代傳統的同步鎖,以提升並發訪問性能
            讀寫鎖是可重入、可降級的,一個線程獲取讀寫鎖後,可以繼續遞歸獲取;從寫鎖可以降級為讀鎖,以便快速釋放鎖資源
            ReentrantReadWriteLock 支持獲取鎖的公平策略,在某些特殊的應用場景下,可以提升並發訪問的性能,同時兼顧線程等待公平性
            讀寫鎖支持非阻塞的嘗試獲取鎖,如果獲取失敗,直接返回false,而不是同步阻塞,這個功能在一些場景下非常有用。例如多個線程同步讀寫某個資源,當發生異常或者需要釋放資源的時候,由哪個線程釋放是個挑戰,因為某些資源不能重復釋放或者重復執行,這樣,可以通過tryLock 方法嘗試獲取鎖,如果拿不到,說明已經被其它線程占用,直接退出即可
            獲取鎖之後一定要釋放鎖,否則會發生鎖溢出異常。通常的做法是通過finally 塊釋放鎖。如果是tryLock,獲取鎖成功才需要釋放鎖
    不要使用線程優先級
        當有多個線程同時運行的時候,由線程調度器來決定哪些線程運行、哪些等待以及線程切換的時間點,由於各個操作系統的線程調度器實現大相徑庭,因此,依賴JDK 自帶的線程優先級來設置線程優先級策略的方法是錯誤和非平臺可移植的
        所以,在任何情況下,你的程序都不能依賴JDK 自帶的線程優先級來保證執行順序、比例和策略
高性能的系列化框架
    高性能框架的特點
        系列化後碼流的大小-寬帶的占用
        系列化和反系列化的性能-CPU資源占用
        是否支持跨語言
    JDK自身系列化性能太差
    性能較高的系列化框架
        Google Protobuf
        Thrift
        Hessian
靈活的TCP參數配置能力
    SO_RCVBUF和SO_SENDBUF設置發送接受緩存大小
    軟中斷等
Netty API
    bootstrap
        Bootstrap:ChannelFactory,ChannelPipeline,ChinnelPipelineFactory
            初始化channel輔助類
            為具體的子類提供公共數據結構
        ServerBootstrap:bind()
            創建服務器端channel輔助類
            接受connection請求
        ClientBootstrap:connect()
            創建客戶端channel輔助類
            發起connection請求
        ConnectionlessBootstrap:connect(),bind()
            創建無連接傳輸channel輔助類(UDP)
            包括Client和Server
    buffer
        取代JDK NIO的java.nio.ByteBuffer,相比ByteBuffer
            可以根據需要自定義buffer type
            內置混合的buffer type,實現zero-copy
            提供類似StringBuffer的動態dynamic buffer
            不需要調用flip方法
            更快的性能
        推薦使用ChannelBuffers靜態工廠創建ChannelBuffer
    Channel
        channel
            channel核心API,包括異步和事件驅動等各種傳送接口
        group
            channel group,幫助用戶維護channel列表
        local
            一種虛擬傳輸方式,允許一個虛擬機上的兩個部分可以相互通信
        socket
            TCP,UDP接口,集成了核心的channel API
        socket oio
            基於老io的socket channel實現
        socket HTTP
            基於http客戶端和相應的server端實現,工作在有防火墻的情況下
    handler
        handler
            處理器
        codec
            編碼解碼器
            base64
                Base64編碼
            compression
                壓縮格式
            embedder
                嵌入式下編碼和解碼
            frame
                評估流的數據的排列和內容
            http.websocket
                websocket編碼解碼
            http
                http的編碼解碼以及類型信息
            oneone
                對象到對象編碼解碼
            protobuf
                Protocol Buffers的編碼解碼
            replay
                在阻塞IO中實現非阻塞解碼
            rtsp
                RTSP的編碼解碼
            serialization
                系列化對象到bytebuffer的實現
            string
                字符串編碼解碼,繼承oneone
        execution
            基於Executor的實現
        queue
            將event存入內部隊列的處理
        ssl
            基於SSLEngine的SSL以及TLS實現
        stream
            異步寫入大數據,不會產生outOfMemory也不會花費很多內存
        timeout
            通過Timer來對讀寫超時或者閑置鏈接進行通知
Netty線程問題案例
    Netty 3版本升級遭遇內存泄漏案例
        業務代碼升級Netty 3到Netty4之後,運行一段時間,Java進程就會宕機,查看系統運行日誌發現系統發生了內存泄露
        從業務的使用方式入手分析
            內存的分配是在業務代碼中進行,由於使用到了業務線程池做I/O操作和業務操作的隔離,實際上內存是在業務線程中分配的
            內存的釋放操作是在outbound中進行,按照Netty 3的線程模型,downstream(對應Netty 4的outbound,Netty 4取消了upstream和downstream)的handler也是由業務調用者線程執行的,也就是說申請和釋放在同一個業務線程中進行。初次排查並沒有發現導致內存泄露的根因,繼續分析Netty內存池的實現原理
            Netty 內存池實現原理分析:查看Netty的內存池分配器PooledByteBufAllocator的源碼實現,發現內存池實際是基於線程上下文實現的
        問題根因
            Netty 4修改了Netty 3的線程模型:在Netty 3的時候,upstream是在I/O線程裏執行的,而downstream是在業務線程裏執行。當Netty從網絡讀取一個數據報投遞給業務handler的時候,handler是在I/O線程裏執行;而當我們在業務線程中調用write和writeAndFlush向網絡發送消息的時候,handler是在業務線程裏執行,直到最後一個Header handler將消息寫入到發送隊列中,業務線程才返回
            Netty 4修改了這一模型,在Netty 4裏inbound(對應Netty 3的upstream)和outbound(對應Netty 3的downstream)都是在NioEventLoop(I/O線程)中執行。當我們在業務線程裏通過ChannelHandlerContext.write發送消息的時候,Netty 4在將消息發送事件調度到ChannelPipeline的時候,首先將待發送的消息封裝成一個Task,然後放到NioEventLoop的任務隊列中,由NioEventLoop線程異步執行。後續所有handler的調度和執行,包括消息的發送、I/O事件的通知,都由NioEventLoop線程負責處理
            在本案例中,ByteBuf在業務線程中申請,在後續的ChannelHandler中釋放,ChannelHandler是由Netty的I/O線程(EventLoop)執行的,因此內存的申請和釋放不在同一個線程中,導致內存泄漏
    Netty 3版本升級性能下降案例
        業務代碼升級Netty 3到Netty4之後,並沒有給產品帶來預期的性能提升,有些甚至還發生了非常嚴重的性能下降,這與Netty 官方給出的數據並不一致
        在Netty 3中,上述兩個熱點方法都是由業務線程負責執行;而在Netty 4中,則是由NioEventLoop(I/O)線程執行。對於某個鏈路,業務是擁有多個線程的線程池,而NioEventLoop只有一個,所以執行效率更低,返回給客戶端的應答時延就大。時延增大之後,自然導致系統並發量降低,性能下降
        找出問題根因之後,針對Netty 4的線程模型對業務進行專項優化,將耗時的編碼等操作遷移到業務線程中執行,為I/O線程減負,性能達到預期,遠超過了Netty 3老版本的性能
        該問題的根因還是由於Netty 4的線程模型變更引起,線程模型變更之後,不僅影響業務的功能,甚至對性能也會造成很大的影響
        對Netty的升級需要從功能、兼容性和性能等多個角度進行綜合考慮,切不可只盯著API變更這個芝麻,而丟掉了性能這個西瓜。API的變更會導致編譯錯誤,但是性能下降卻隱藏於無形之中,稍不留意就會中招
    Netty業務Handler接收不到消息案例
        碰到一個問題,經常有請求上來到MessageDecoder就結束了,沒有繼續往LogicServerHandler裏面送,覺得很奇怪,是不是線程池滿了
        Netty EventExecutor的典型實現有兩個:DefaultEventExecutor和SingleThreadEventLoop,在本案例中,因為使用的是DefaultEventExecutorGroup,所以實際執行業務Handler的線程池就是DefaultEventExecutor,它繼承自SingleThreadEventExecutor,從名稱就可以看出它是個單線程的線程池。它的工作原理如下
            DefaultEventExecutor聚合JDK的Executor和Thread, 首次執行Task的時候啟動線程,將線程池狀態修改為運行態
            Thread run方法循環從隊列中獲取Task執行,如果隊列為空,則同步阻塞,線程無限循環執行,直到接收到退出信號
            事實上,Netty為了防止多線程執行某個Handler(Channel)引起線程安全問題,實際只有一個線程會執行某個Handler
        實際就像JDK的線程池,不同的業務場景、硬件環境和性能標就會有不同的配置,無法給出標準的答案。需要進行實際測試、評估和調優來靈活調整
    Netty 4 ChannelHandler線程安全疑問
        Netty 4優化了Netty 3的線程模型,其中一個非常大的優化就是用戶不需要再擔心ChannelHandler會被並發調用,總結如下
            ChannelHandler‘s的方法不會被Netty並發調用
            用戶不再需要對ChannelHandler的各個方法做同步保護
            ChannelHandler實例不允許被多次添加到ChannelPiple中,否則線程安全將得不到保證
        ChannelHandler的線程安全存在幾個特例,總結如下
            如果ChannelHandler被註解為 @Sharable,全局只有一個handler實例,它會被多個Channel的Pipeline共享,會被多線程並發調用,因此它不是線程安全的
            如果存在跨ChannelHandler的實例級變量共享,需要特別註意,它可能不是線程安全的。


3,Netty應用場景

彈性伸縮的分布式服務架構
    阿裏巴巴Dubbo內部私有通信協議-dubbo協議默認使用Netty作為高性能異步通信框架,為分布式服務節點之間提供高性能的NIO客戶端和服務端通信
    大眾點評服務框架Pigeon和消息中間件Swallow
大數據領域
    Hadoop的子系統Apache Avro
        豐富的數據結構
        壓縮、高效、二進制的序列化框
        遠程服務調用(RPC)
        多語言、靈活的集成能力
遊戲行業
    高並發:由於采用異步非阻塞模式,一個Netty遊戲服務端可以同時處理成千上萬的遊戲玩家登陸和在線
    高性能:Netty的性能在各個NIO框架中最高,它的單節點吞吐量非常大,適合海量玩家同時在線遊戲
    安全性:支持HTTPS、SSL等,可以在傳輸層進行安全控制
    定制性:可以方便的實現業務邏輯的定制、遊戲編解碼的定制,可以方便的與第三方進行集成,例如amf3等。




4,Netty結構
5, Reactor單線程模型
6, Rector多線程模型
7, 主從Reactor線程模型












來源:https://blog.csdn.net/zhiguozhu/article/details/50517551



Netty(RPC高性能之道)原理剖析