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

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

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同時支援inboundoutbound事件
  • 第三層
    • 業務邏輯編排層,業務邏輯編排層通常有兩類:一類是純粹業務邏輯編排,還有一類是應用層協議外掛,用於協議相關的編解碼鏈路管理,例如 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 APIbootstrap
  • 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 4inbound(對應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執行緒模型

相關推薦

NettyRPC高效能原理剖析

1,Netty簡述Netty 是一個基於 JAVA NIO 類庫的非同步通訊框架,用於建立非同步非阻塞、基於事件驅動、高效能、高可靠性和高可定製性的網路客戶端和伺服器端RPC高效能分析,請參考文章“【總結】RPC效能之道 ”特點非同步、非阻塞、基於事件驅動的NIO框架支援多種

【總結】NettyRPC高效能原理剖析

1,Netty簡述 Netty 是一個基於 JAVA NIO 類庫的非同步通訊框架,用於建立非同步非阻塞、基於事件驅動、高效能、高可靠性和高可定製性的網路客戶端和伺服器端 RPC高效能分析,請參考文章“【總結】RPC效能之道 ” 特點 非同步、非阻塞、

人臉表情識別筆記特徵提取LBP區域性二值模式原理及MATLAB程式碼

一:原理部分 LBP(Local Binary Pattern,區域性二值模式)是一種用來描述影象區域性紋理特徵的運算元;它具有旋轉不變性和灰度不變性等顯著的優點。它是首先由T. Ojala, M.Pietikäinen, 和D. Harwood 在1994年提出,用

系統間通訊方式RPC的基本概念

1、概述 經過了詳細的資訊格式、網路IO模型的講解,並且通過JAVA RMI的講解進行了預熱。從這篇文章開始我們將進入這個系列博文的另一個重點知識體系的講解:RPC。在後續的幾篇文章中,我們首先講解RPC的基本概念,一個具體的RPC實現會有哪些基本要素構成,然後我們詳細介紹一款典型的RPC框架:Apac

MS17-010 "Eternal Blue永恒”, 修復補丁下載匯總地址!

eternal blue 永恒之藍 ms17-010 4012598 4012212 MS17-010 "Eternal Blue(永恒之藍)”官方修復補丁,下載連接匯總地址 Product (Down-level)Release DateCDN LinkSecurity Update

LeetCode 59. Spiral Matrix II 螺旋矩陣

question hms 參考 ger should rate spa 條件 日期 Given an integer n, generate a square matrix filled with elements from 1 to n2 in spiral order

LeetCode 90. Subsets II 子集合

blank public fun res 東北 cau 利用 租房 .so Given a collection of integers that might contain duplicates, nums, return all possible subsets. N

windows server 2008&R2 復制問題RPC服務不可用

有客 徹底 size 通信 server 外部 alt 運行 啟動 前段時間,有客戶反饋有1臺輔助域控復制有問題,報RPC服務不可用打開事件查看器,發現很多event id 為6013事件,系統啟動時間:45626584秒,大約是528天,系統運行時間超過了497天 Win

ZoomEye鐘馗搜索技巧記錄:

outer 名稱 zoom AI route 進行 service 城市 key 做個記錄方便查看 鐘馗之眼: 指定搜索的組件: app:組件名稱 ver:組件版本 例:搜索 apache組件版本2.4:app:apache var:2.4指定搜素的端口:

Netty 高性能 - Recycler 對象池的復用

設置 protect 表結構 state java ner 節點 obj 更新 前言 我們知道,Java 創建一個實例的消耗是不小的,如果沒有使用棧上分配和 TLAB,那麽就需要使用 CAS 在堆中創建對象。所以現在很多框架都使用對象池。Netty 也不例外,通過重用對象

配置taBar所遇見的問題踩坑

分享圖片 type http 效果圖 navi src 之路 com nbsp 目前效果圖: 問題:我遇見一個問題,點擊每周關註的時候,他應該跳轉到哪一個頁面。在沒有設置taBar還是可以跳轉的。 解決方法是: 修改 open-type=‘navig

2017年第十一屆省賽信陽師範學院

         帶著忐忑和激動的心情,我踏上了參加省賽的路上,對於大一的我來說能參加此次省賽還是很幸運的,我知道目前我的水平不高,去參加省賽無疑是被虐的,但是內心還是想知道自己差了多少,又究竟能承受多大的打擊

disruptor 高效能

disruptor是一個高效能的執行緒間非同步通訊的框架,即在同一個JVM程序中的多執行緒間訊息傳遞。應用disruptor知名專案有如下的一些:Storm, Camel, Log4j2,還有目前的美團點評技術團隊也有很多不少的應用,或者說有一些借鑑了它的設計機制。 下面就跟著筆者一起去領略下disru

文字快速分類利器fasttext使用心得踩坑

fasttext是文字分類的一大利器,優點:快,嗷嗷快;缺點:暫未發現。但是我在使用其做文字分類時候還是遇到了挺多坑,今天先總結一個: 網上有人說設定訓練引數的時候,ngrams設定大於2可以提高模型的精確度,打算試試,然而設定之後系統直接顯示: floating point exception(core

Android studio匯入Netty修改maven倉庫Url

最近在接手一個Android開發專案,要求手機作為熱點,接入ARM裝置,作為Server通過TCP/IP協議與下位機Socket通訊,簡單通過Service實現了封裝Socket函式並保持後臺連線,但在收發資料時需要對接相應的協議,這一部分協議是由下位機決定的,裡面包含了一些結構體Struct和聯合

因式分解機Factorization Machine,FM原理及實踐

因子分解機(Factorization Machine),是由Konstanz大學(德國康斯坦茨大學)Steffen Rendle(現任職於Google)於2010年最早提出的,旨在解決大規模稀疏資料下的特徵組合問題。原論文見此。 不久後,FM的升級版模型場感知分解機(Field-awa

快樂的跳躍者相鄰數

如果有n個數, 這n個數中相鄰數之間的差的絕對值能夠取遍[1,n-1]之間所有的整數.那麼就輸出OK否則輸出NO; 例如:1 4 2 3 就是是'OK' ,因為相鄰數之差的絕對值分別為3、2、1。 輸入 有T組測試資料。輸入的第一行是資料組數T,其後每一行是一組測試資料。每行包含一個

隨想錄人臉檢測dlib

【 宣告:版權所有,歡迎轉載,請勿用於商業用途。 聯絡信箱:feixiaoxing @163.com】       opencv大家用的很多,但是opencv的效率實在不敢恭維。所以,大家開始慢慢尋找其他的一些開源庫,dlib就是不錯的一個選擇。當然,ope

Python爬蟲六:字型反爬處理貓眼+汽車-2018.10

環境:Windows7 +Python3.6+Pycharm2017 目標:貓眼電影票房、汽車之家字型反爬的處理                   --------全部文章: 京東爬蟲 、鏈家爬蟲、美團爬蟲、微信公眾號爬蟲、字型反爬--------- 前言:字型反爬,

Python入門筆記—第六章面向物件OOP

第六章:面向物件之OOP 1.面向物件的概述(Object Oriented,OO) 1.1 名詞解釋 OO(Object Oriented):面向物件 OOA(Object Oriented Analysis):面向物件的分析 OOD(Object Oriente