1. 程式人生 > >​結合非同步模型,再次總結Netty多執行緒編碼最佳實踐

​結合非同步模型,再次總結Netty多執行緒編碼最佳實踐

更多技術分享可關注我

前言

本文重點總結Netty多執行緒的一些編碼最佳實踐和注意事項,並且順便對Netty的執行緒排程模型,和非同步模型做了一個彙總。原文:​​結合非同步模型,再次總結Netty多執行緒編碼最佳實踐

Netty多執行緒編碼的最佳實踐總結

接該文:Netty的執行緒排程模型分析(10)《Netty多執行緒開發的最佳實踐有哪些?》

回憶:

1、服務端需要啟動兩個NioEventLoopGroup,其中boss(新連線接入)執行緒池大小設定為1即可,設定多了也是1個I/O執行緒在起作用,而且還浪費記憶體。

2、如果業務非常簡單,執行時間非常短,不需要與外部網元互動、比如訪問資料庫等,也不需要等待其它資源,那麼建議直接在業務ChannelHandler中執行,不需再啟動Netty的非I/O執行緒池或者使用額外的執行緒池,避免大量CPU上下文切換的開銷,而且也不存線上程安全問題

3、如果業務邏輯耗時較大,或者是時間不可控的業務,比如查詢資料庫,那麼建議封裝為task後,投遞到後端的非I/O執行緒池統一處理,可以使用Netty預設提供的無鎖序列化執行緒池——DefaultEventExecutorGroup,在新增handler的時候繫結即可,或者直接投遞到另外的程序中處理,比如訊息佇列,最後把計算結果傳送給Netty的I/O執行緒即可,如果使用了非I/O執行緒驅動耗時邏輯,那麼再傳遞結果時,Netty的I/O執行緒會判斷當前執行緒是不是I/O執行緒,如果不是,那麼Netty會自動將該邏輯封裝為task扔到MPSCQ,並讓I/O執行緒驅動,因此不用擔心業務執行緒池的結果無法返回到I/O執行緒

4、過多的業務ChannelHandler會帶來開發效率和可維護性問題,不要把Netty當作業務容器,對於大多數複雜的業務產品,仍然需要整合或者開發自己的業務容器,做好和Netty的架構分層。

 

額外補充幾點:

1、善於使用Netty的鉤子方法:參考:Netty的非同步模型分析(5)

2、儘量不要在ChannelHandler中啟動使用者執行緒(解碼訊息並派發訊息到非I/O執行緒池除外)

3、解碼處理器應該被NIO執行緒呼叫,不要切換到使用者執行緒

4、一個EventLoop在它的整個生命週期當中都只會與唯一一個永遠不會被改變的Thread進行繫結,所有由EventLoop處理的I/O事件都將在它所關聯的那個Thread上進行處理,一個Channel在它的整個生命週期中只會註冊在一個EventLoop上,一個EventLoop在執行過程當中,會被分配給一個或者多個Channel,得出重要結論:在Netty中,Channel的實現一定是執行緒安全的,基於此,使用者可以儲存一個Channel的引用,並且在需要向遠端建點發送資料時,通過這個引用來呼叫Channel相應的方法,即便當時有很多執行緒都在使用它也不會出現執行緒不安全問題,而且訊息一定會按順序發出去。

5、千萬理解Netty的NIO執行緒的模型,它的樣子,如果是非同步API被NIO執行緒驅動,那麼該API本質還是序列的,因為NIO執行緒是非同步序列無鎖化模型,要想實現非序列執行,必須將非同步API封裝為task,讓非I/O執行緒驅動。當然對於簡單的收發訊息,大可不必這樣笨拙,大膽的讓NIO執行緒驅動即可。具體參考:Netty的writeAndFlush()非同步實現原始碼分析和正確用法

6、可以使用過載的addLast方法,在向pipeline新增handler時,傳入Netty提供的非I/O執行緒池——DefaultEventExecutor,此後該handler上的事件,都是傳入的group執行緒池來執行。具體參考:Netty耗時的業務邏輯應該寫在哪兒,有什麼注意事項?

7、強烈建議使用Netty的非同步API時,都為其附加一個Netty的監聽器,監聽非同步I/O的結果,儘量少用非同步轉同步API,即NIO執行緒能不阻塞就不阻塞。

8、如果要使用非同步轉同步API,那麼必須使用非NIO執行緒驅動,否則會死鎖,具體原因見:Netty非同步API轉同步的實現原理和正確用法

個人階段性感想暨對Netty的認識總結

從18年年中接觸Netty,讀過《Netty實戰》,《Netty權威指南》,《跟著案例學Netty》,以及閃電俠的原始碼分析課程,直到自己通過demo入手通讀了它的原始碼,到現在已經快兩年了,期間搞過Netty的小輪子,比如仿微信聊天伺服器,HTTP長連線客戶端,簡單的分散式RPC框架,有了這些經歷才催生了這一系列筆記文章。

水平一般,能力有限,就我個人理解,對Netty的抽象如下:

 

 

理解Netty的核心就是4個影象,或者說模型也OK,之所以我現在叫它們影象,是因為將這些機制想象抽象為圖形,比較容易理解和加深印象,分別是:

 

1、執行緒排程影象

即對epoll機制,NIO三大元件(I/O多路複用器Selector+Channel+Buffer)和基本的網路程式設計流程,Reactor模型,和Java多執行緒編碼的把握。首先,需要知道BIO和NIO的區別,NIO三大件的特點和用途,原生NIO的bug,比如臭名昭著的epoll空輪詢bug、還有一些坑,比如處理I/O事件需要移除key,I/O多路複用器(Selector)返回的集合不是執行緒安全的,如何正確的給Selector註冊Channel和更新感興趣的I/O事件,如何正確的處理寫事件,如何正確的處理連線事件,如何正確的取消註冊Channel等,太多了,以致於能深刻理解為何Netty會這麼火。之後就是對Reactor三種模型的正確理解,尤其是主從模型的理解,以及Netty預設使用的是多執行緒Reactor模型。然後就是對JUC的使用,常見無鎖工具的理解,如何配置I/O執行緒池,Netty的NIO執行緒的事件迴圈機制等,最後就是對Linux的5種I/O模型的認識,尤其是I/O多路複用模型中的epoll機制的深刻把握。

對應Netty就是Channel,Unsafe,EventLoop(Group)元件。

 

2、非同步影象

即把握對觀察者設計模式,多執行緒設計模式中的Future,承諾模式的理解,還有JUC的管程,鎖的機制的理解等。

對應Netty就是Future和Promise元件。

 

3、流水線影象(pipeline+事件回撥影象)

這是後續要分析總結的pipeline模型和事件回撥機制,這部分需要知道Netty的pipeline模型和職責鏈設計模式的區別,pipeline的雙向連結串列結構,入站和出站處理器的設計理念和各個回撥事件的正確使用,還有一些Netty已經實現好的常用的入站,出站處理器的正確使用和理解。

對應Netty就是ChannelPipeline和ChannelHandler元件

 

4、記憶體影象

也是後續要分析總結的,核心就是把握Netty的ByteBuf設計理念,和NIO的Buffer的缺陷,重點是ByteBuf的記憶體池設計思想,Netty垃圾回收計數器的設計和編碼注意事項,堆外記憶體的使用套路,這部分重要程度和執行緒排程模型並駕齊驅,甚至還在之上,可以說深刻掌握了記憶體影象才算掌握了Netty,需要知道Netty有哪些記憶體型別,不同型別不同大小的記憶體是如何分配的,不同型別記憶體的回收策略,如何避免OOM,如何減少多執行緒對記憶體分配的競爭,所謂的Netty的零拷貝和作業系統的零拷貝的區別等。

 

以上,掌握了這幾個影象,個人認為Netty就應該算掌握的比較不錯了,剩下的就是對Netty的高效能工具類的學習和對其實現原始碼的把握,比如時間輪工具——HashedWheelTimer,改進的FastThreadLocal,記憶體池,常量池,@Sharable註解在何時可以使用以及坑。

 

進一步,需要掌握Netty的一些高階特性的實現原理和用法,比如空閒檢測handler,並且知道如何設計長連線以及對應的心跳包和應用層心跳的必要性,知道如何斷鏈重連,如何設計重複登入保護機制,還要知道Netty所提供的TCP協議的粘包半包處理器的使用方法和底層原理,流控和流量整形的使用方法,並且知道流控和流量整形的區別以及各自的實現機制。

 

再進一步,就是熟悉各種開源框架中如何使用的Netty,比如Zookeeper,Kafka,Elasticsearch,gRpc等開源框架是怎麼用Netty的。

 

個人應該正處在上個階段。最後再往下鑽只能先拋磚引玉,因為可能單純靠看原始碼或者文章會力不從心,至少我是這樣感覺的,所以需要進一步用實際的高併發專案打磨。比如IM專案,遊戲伺服器,推送服務等,這樣應該能更深刻的理解比如私有協議的一般設計套路,壓縮合並handler的技巧,單機百萬長連線的調優過程,即著名的C10K到C10M問題,Netty的效能指標和監控策略,需要對Linux作業系統和TCP協議有一定認識,比如常用的TCP引數的深入理解,可以通過如下幾個問題,也是我收集的很常見的面試題來自我評判,後續專題總結出來:

1、TCP協議如何保證可靠性

2、TCP協議如何對傳送的資料進行分段?

3、TCP協議有幾種狀態,都是什麼,是如何轉換的?

4、三次握手中,如果服務端TCP狀態為SYN_RECEV態,此時發普通資料包給伺服器,伺服器會怎麼處理?

5、TCP連線何時會進入TIME-WAIT態,該狀態會如何轉移?

6、TCP四次分手,最後為什麼要等待2MSL後才關閉連線,為何不是4MSL或者其它時間?

7、TCP連線的關閉過程由於存在TIME-WAIT態,這會影響其他伺服器程式在該埠建立TCP連線嗎?

8、TCP被動關閉方如果總收不到對端最後一個ACK,那會一直重傳FIN段麼?

9、TCP主動關閉方有沒有可能等待2MSL後,收到對端的超時重傳FIN報文?

10、如何理解IP資料報的TTL?

11、TCP的被動關閉端為什麼不需要類似TIME_WAIT的狀態?

12、什麼是TCP的全連線,半連線佇列?

13、如何關閉TCP連線,怎麼優雅的關閉?

14、為什麼TCP有一個SO_REUSEADDR引數,它對網路程式設計有什麼影響?

15、伺服器TIME_WAIT狀態過多怎麼辦,如何定位並解決?

16、TCP的RST標誌位在何時會出現?

17、TCP的RST攻擊是怎麼回事?

18、Java的IOException:Connection reset by peer的真正原因是什麼?

19、TCP的SYN Flood攻擊是怎麼回事,如何解決?

20、TCP中已有SO_KEEPALIVE選項,為什麼還在應用層加入心跳機制?

21、TCP如何處理小塊的資料流(涉及nagle演算法)?

22、TCP如何處理大塊資料流(涉及滑動視窗,擁塞控制演算法)?

23、TCP協議的7個定時器是哪幾個,分別在什麼條件下起作用?

24、TCP協議的應用層的粘包、分包問題和解決方案

25、TCP協議存在哪些缺陷?

26、TCP/IP和HTTP的區別和聯絡?

27、一個應用最多可以支援多少TCP併發連線?

28、單機百萬長連線的調優過程,涉及作業系統的一些引數+TCP引數+Netty引數+JVM引數的配置

29、某個應用的CPU使用率很高,甚至達到100%,應該怎麼處理?

30、什麼是殭屍程序,大量的殭屍程序或者不可中斷程序該怎麼處理?

31、如何分析CPU的瓶頸?

32、伺服器的記憶體swap變高了應該怎麼處理?

33、JVM發生了OOM該怎麼定位和處理,有哪些命令和工具?

34、。。。

 

以上,篇幅有限,想到哪兒寫到哪兒,關於Netty的執行緒排程,I/O多路複用器,非同步API的拆解算是告一段落,接下來的幾篇文章分析總結的是Netty服務端新連線接入的過程和Netty的pipeline+事件回撥機制。

 

4句詩與君共享:

1、問渠那得清如許,為有源頭活水來

2、沉舟側畔千帆過,病樹前頭萬木春

3、革命尚未成功,同志仍需努力

4、低頭做事,抬頭看路,既要深入細節,又要跳出來看全域性

 

後記

dashuai的部落格是終身學習踐行者,大廠程式設計師,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於網際網路行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 

相關推薦

結合非同步模型再次總結Netty執行編碼最佳實踐

更多技術分享可關注我 前言 本文重點總結Netty多執行緒的一些編碼最佳實踐和注意事項,並且順便對Netty的執行緒排程模型,和非同步模型做了一個彙總。原文:​​結合非同步模型,再次總結Netty多執行緒編碼最佳實踐 Netty多執行緒編碼的最佳實踐總結 接該文:Netty的執行緒排程模型分析(10)《

Java執行併發最佳實踐

使用本地變數 儘量使用本地變數,而不是建立一個類或例項的變數。 使用不可變類 String、Integer等。不可變類可以降低程式碼中需要的同步數量。 最小化鎖的作用域範圍:S=1/(1-a+a/n) a:平行計算部分所佔比例 n:並行處理結點個數 S:加速比 當1-a等於0時,沒有序列只有並

Java執行安全策略與執行併發最佳實踐

  執行緒安全策略 不可變物件 不可變物件(Immutable Objects)是指物件一旦被建立它的狀態(

執行併發最佳實踐

1、使用本地區域性變數; 2、使用不可變類 3、最小化鎖的作用範圍; 4、使用Excutor而不是thread; 5、寧可

jdk8新特性(Lambda表示式)結合spring 執行一行程式碼實現執行

1.配置spring 執行緒池 @Configuration @EnableAsync @ConfigurationProperties(prefix="threadpool") public class ExecutePoolConfiguration { @V

boost中asio網路庫執行併發處理實現以及asio在執行模型執行的排程情況和執行安全。

1、實現多執行緒方法: 其實就是多個執行緒同時呼叫io_service::run         for (int i = 0; i != m_nThreads; ++i)         {             boost::shared_ptr<boost::

day24總結_執行和設計模式

1、多執行緒 ①、JDK5以後的針對執行緒的鎖定操作和釋放操作 // 定義鎖物件 private Lock lock = new ReentrantLock(); // 加鎖 lock.lock(); // 釋放鎖 lock.unlock(); ②、死鎖問題的描述和程式碼體現 *死鎖:兩個或

總結-Java執行與高併發簡記

1、什麼是多執行緒? 一個程序可以開啟多個執行緒,每個執行緒可以併發/並行執行不同任務。 2、Java多執行緒實現方式    2.1、繼承Thread類    2.2、實現Runnable介面方式實現多執行緒    2.3、使

#Java執行學習那麼如何實現執行呢?

程序和執行緒的區別: 程序:每個程序都有獨立的程式碼和資料空間(程序上下文),程序間的切換會有較大的開銷,一個程序包含1–n個執行緒。 執行緒:同一類執行緒共享程式碼和資料空間,每個執行緒有獨立的執行棧和程式計數器(PC),執行緒切換開銷小。 執行緒和程序一樣分

以單例模式為例在Idea中執行debug

我們以單例模式的懶漢式在idea中進行多執行緒debug 一是可以學習多執行緒debug,二是可以瞭解懶漢式的執行緒不安全的原因 首先我們建立一個單例懶漢式,然後建立兩個執行緒 程式碼如下:   然後 進行多執行緒debug,來干預懶漢式的執行順序

Python爬蟲從入門到精通(3): BeautifulSoup用法總結執行爬蟲爬取糗事百科

本文是Python爬蟲從入門到精通系列的第3篇。我們將總結BeautifulSoup這個解析庫以及常用的find和select方法。我們還會利用requests庫和BeauitfulSoup來爬取糗事百科上的段子, 並對比下單執行緒爬蟲和多執行緒爬蟲的爬取效率。 什麼是

Java執行學習---------超詳細總結(java 執行 同步 資料傳遞 )

平時專案中多執行緒的應用比較少,今天網上找了找相關的內容學習了下。看到下面的文章感覺比較好。 轉自:http://www.cnblogs.com/1020182600HENG/p/5939933.html 目錄 一擴充套件javalangThread類 二實現javalan

生產電腦取走電腦執行

public class ThreadDemo{     public static void main(String[] args) {         Resource res = new Resource

作為一個java初學者改如何學習執行

多執行緒相對於其他 Java 知識點來講,有一定的學習門檻,並且瞭解起來比較費勁。在平時工作中如若使用不當會出現資料錯亂、執行效率低(還不如單執行緒去執行)或者死鎖程式掛掉等等問題,所以掌握瞭解多執行緒至關重要。 1、為什麼要使用多執行緒 首先,你學習Java多執行緒,得知道為什麼要使

Github專案NettyRpc 閱讀(Netty+執行+AQS+CAS+volatile)

Github專案:https://github.com/luxiaoxun/NettyRpc Fork: https://github.com/sw008/NettyRpc 此專案很適合學習多執行緒和Netty RPC呼叫流程 大體思路:客戶端通過ConcurrentHas

再次學習MFC執行及同步

一、MFC對多執行緒程式設計的支援   MFC中有兩類執行緒,分別稱之為工作者執行緒和使用者介面執行緒。二者的主要區別在於工作者執行緒沒有訊息迴圈,而使用者介面執行緒有自己的訊息佇列和訊息迴圈。  工作者執行緒沒有訊息機制,通常用來執行後臺計算和維護任務,如冗長的計算過程,印表機的後臺列印等。使用者介面執行

終於理解單列模式了單列模式和執行

//1、單例類確保自己只有一個例項(構造方法私有化)//2、單例類必須自己建立自己的例項。//3、單例類必須為其他物件提供唯一的例項。package Singleton;//懶漢式  單例例項在第一次被使用時構建,延遲初始化。public class singleton { 

9--黑馬程式設計師--技術總結執行

、期待與您交流! ---------------------- 一.多執行緒的概念 以往開發的程式大多是單執行緒的,即一個程式只有一條從頭至尾的執行線索。然而現實世界中的很多過程都具有多條線索同時動作的特性:例如,我們可以一邊看電視,一邊活動胳膊,如果

再次理解java執行的實現 Thread 和Runnable的區別

Thread和Runnable的區別如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable介面的話,則很容易的實現資源共享。main函式,例項化執行緒物件也有所不同,extends Thread :t.start();implements Runnable

spring 非同步方法(@Async註解代替執行

最近在開發過程裡遇到讓人很頭痛的功能,就是一個批量複製功能,批量複製中包括資料庫中的資料,還有檔案系統的複製。這在開發中要考慮到系統性能和友好度的問題,一個批量複製最少要執行1~3分鐘,這讓使用者在點選一個按鈕後要等待1~3分鐘不現實,最後只能用多執行緒,來達到使用者的友好度