Netty相關知識彙總
1、TCP、UDP的區別?
TCP與UDP區別總結:
1)、TCP面向連線(如打電話要先撥號建立連線);UDP是無連線的,即傳送資料之前不需要建立連線。
2)、TCP提供可靠的服務。也就是說,通過TCP連線傳送的資料,無差錯,不丟失,不重複,且按序到達;UDP盡最大努力交付,即不保證可靠交付
3)、TCP面向位元組流,實際上是TCP把資料看成一連串無結構的位元組流;UDP是面向報文的 UDP沒有擁塞控制,因此網路出現擁塞不會使源主機的傳送速率降低(對實時應用很有用,如IP電話,實時視訊會議等)
4)、每一條TCP連線只能是點到點的;UDP支援一對一,一對多,多對一和多對多的互動通訊
5)、TCP首部開銷20位元組;UDP的首部開銷小,只有8個位元組
6)、TCP的邏輯通訊通道是全雙工的可靠通道,UDP則是不可靠通道
2、TCP協議如何保證可靠傳輸?
3、TCP的握手、揮手機制?
TCP的握手機制:
TCP的揮手機制:
詳情參考文章: blog.csdn.net/qzcsu/artic…
4、TCP的粘包/拆包原因及其解決方法是什麼?
為什麼會發生TCP粘包、拆包? 發生TCP粘包、拆包主要是由於下面一些原因:
1).應用程式寫入的資料大於套接字緩衝區大小,這將會發生拆包。
2).應用程式寫入資料小於套接字緩衝區大小,網路卡將應用多次寫入的資料傳送到網路上,這將會發生粘包。
3).進行MSS(最大報文長度)大小的TCP分段,當TCP報文長度-TCP頭部長度>MSS的時候將發生拆包。
4).接收方法不及時讀取套接字緩衝區資料,這將發生粘包。
粘包、拆包解決辦法:
TCP本身是面向流的,作為網路伺服器,如何從這源源不斷湧來的資料流中拆分出或者合併出有意義的資訊呢?通常會有以下一些常用的方法:
1)、傳送端給每個資料包新增包首部,首部中應該至少包含資料包的長度,這樣接收端在接收到資料後,通過讀取包首部的長度欄位,便知道每一個資料包的實際長度了。
2)、傳送端將每個資料包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩衝區中讀取固定長度的資料就自然而然的把每個資料包拆分開來。
3)、可以在資料包之間設定邊界,如新增特殊符號,這樣,接收端通過這個邊界就可以將不同的資料包拆分開。
詳情參考文章:www.cnblogs.com/panchanggui…
5、Netty的粘包/拆包是怎麼處理的,有哪些實現?
對於粘包和拆包問題,常見的解決方案有四種:
1)、客戶端在傳送資料包的時候,每個包都固定長度,比如1024個位元組大小,如果客戶端傳送的資料長度不足1024個位元組,則通過補充空格的方式補全到指定長度;Netty提供的FixedLengthFrameDecoder
2)、客戶端在每個包的末尾使用固定的分隔符,例如\r\n,如果一個包被拆分了,則等待下一個包傳送過來之後找到其中的\r\n,然後對其拆分後的頭部部分與前一個包的剩餘部分進行合併,這樣就得到了一個完整的包;Netty提供LineBasedFrameDecoder與DelimiterBasedFrameDecoder
3)、將訊息分為頭部和訊息體,在頭部中儲存有當前整個訊息的長度,只有在讀取到足夠長度的訊息之後才算是讀到了一個完整的訊息;Netyy提供了LengthFieldBasedFrameDecoder與LengthFieldPrepender
4)、通過自定義協議進行粘包和拆包的處理。Netty提供了通過實現MessageToByteEncoder和ByteToMessageDecoder來實現
更詳細請閱讀文章:www.cnblogs.com/AIPAOJIAO/p…
6、同步與非同步、阻塞與非阻塞的區別?
簡單點理解就是:
1). 同步,就是我呼叫一個功能,該功能沒有結束前,我死等結果。
2). 非同步,就是我呼叫一個功能,不需要知道該功能結果,該功能有結果後通知我(回撥通知)
3). 阻塞,就是呼叫我(函式),我(函式)沒有接收完資料或者沒有得到結果之前,我不會返回。
4). 非阻塞,就是呼叫我(函式),我(函式)立即返回,通過select通知呼叫者
同步IO和非同步IO的區別就在於:資料拷貝的時候程式是否阻塞
阻塞IO和非阻塞IO的區別就在於:應用程式的呼叫是否立即返回
7、說說網路IO模型?
8、BIO、NIO、AIO分別是什麼?
BIO:同步並阻塞,伺服器實現模式為一個連線一個執行緒,即客戶端有連線請求時伺服器端就需要啟動一個執行緒進行處理,如果這個連線不做任何事情會造成不必要的執行緒開銷,當然可以通過執行緒池機制改善。BIO方式適用於連線數目比較小且固定的架構,這種方式對伺服器資源要求比較高,併發侷限於應用中,JDK1.4以前的唯一選擇,但程式直觀簡單易理解。
NIO:同步非阻塞,伺服器實現模式為一個請求一個執行緒,即客戶端傳送的連線請求都會註冊到多路複用器上,多路複用器輪詢到連線有I/O請求時才啟動一個執行緒進行處理。NIO方式適用於連線數目多且連線比較短(輕操作)的架構,比如聊天伺服器,併發侷限於應用中,程式設計比較複雜,JDK1.4開始支援。
AIO:非同步非阻塞,伺服器實現模式為一個有效請求一個執行緒,客戶端的I/O請求都是由OS先完成了再通知伺服器應用去啟動執行緒進行處理.AIO方式使用於連線數目多且連線比較長(重操作)的架構,比如相簿伺服器,充分呼叫OS參與併發操作,程式設計比較複雜,JDK7開始支援。
9、select、poll、epoll的機制及其區別?
1).單個程式開啟的檔案描述符(fd檔案控制程式碼)不一致
select :有最大連線數限制數為1024,單個程式所能開啟的最大連線數由FD_ZETSIZE巨集定義。
poll:poll本質上與select沒有區別,但是它沒有最大連線數的限制,原因是它是基於連結串列來儲存的。
epoll:雖然連線有上限,但是很大,1G記憶體的機器可以開啟10萬左右的連線,以此類推。
2).監聽Socket的方式不一致
select :輪詢的方式,一個一個的socket檢查過去,發現有socket活躍時才進行處理,當線性socket增多時,輪詢的速度將會變得很慢,造成線性造成效能下降問題。
poll:對select稍微進行了優化,只是修改了檔案描述符,但是監聽socket的方式還是輪詢。
expoll:epoll核心中實現是根據每個fd上的callback函式來實現的,只有活躍的socket才會主動呼叫callback,通知expoll來處理這個socket。(會將連線的socket註冊到epoll中,相當於socket的花名冊,如果有一個socket活躍了,會回撥一個函式,通知epoll,趕緊過來處理)
3).記憶體空間拷貝方式(訊息傳遞方式)不一致
select:核心想將訊息傳遞到使用者態,需要將資料從核心態拷貝到使用者態,這個過程非常的耗時
poll:同上
epoll:epoll的核心和使用者空間共享一塊記憶體,因此記憶體態資料和使用者態資料是共享的
select、poll、epoll時間複雜度分別是:O(n)、O(n)、O(1)
10、說說你對Netty的瞭解?
這個沒用過的話,就不要死撐了。用過的估計不用找了吧。
11、Netty跟Java NIO有什麼不同,為什麼不直接使用JDK NIO類庫?
說說NIO有什麼缺點吧:
NIO的類庫和API還是有點複雜,比如Buffer的使用 Selector編寫複雜,如果對某個事件註冊後,業務程式碼過於耦合 需要了解很多多執行緒的知識,熟悉網路程式設計 面對斷連重連、保丟失、粘包等,處理複雜 NIO存在BUG,根據網上言論說是selector空輪訓導致CPU飆升,具體有興趣的可以看看JDK的官網
Netty主要的優點有:
框架設計優雅,底層模型隨意切換適應不同的網路協議要求
提供很多標準的協議、安全、編碼解碼的支援
解決了很多NIO不易用的問題
社群更為活躍,在很多開源框架中使用,如Dubbo、RocketMQ、Spark等
底層核心有:Zero-Copy-Capable Buffer,非常易用的靈拷貝Buffer(這個內容很有意思,稍後專門來說);統一的API;標準可擴充套件的時間模型
傳輸方面的支援有:管道通訊(具體不知道幹啥的,還請老司機指教);Http隧道;TCP與UDP
協議方面的支援有:基於原始文字和二進位制的協議;解壓縮;大檔案傳輸;流媒體傳輸;protobuf編解碼;安全認證;http和websocket
總之提供了很多現成的功能可以直接供開發者使用。
12、Netty元件有哪些,分別有什麼關聯?
Channel ----Socket
EventLoop ----控制流,多執行緒處理,併發;
ChannelHandler和ChannelPipeline
Bootstrap 和 ServerBootstrap
更多詳情可以閱讀:blog.csdn.net/summerZBH12…
13、說說Netty的執行流程?
1)、建立ServerBootStrap例項
2)、設定並繫結Reactor執行緒池:EventLoopGroup,EventLoop就是處理所有註冊到本執行緒的Selector上面的Channel
3)、設定並繫結服務端的channel
4)、5)、建立處理網路事件的ChannelPipeline和handler,網路時間以流的形式在其中流轉,handler完成多數的功能定製:比如編解碼 SSl安全認證
6)、繫結並啟動監聽埠
7)、當輪訓到準備就緒的channel後,由Reactor執行緒:NioEventLoop執行pipline中的方法,最終排程並執行channelHandler
更多請參考文章:juejin.im/post/5bf8fb…
14、Netty高效能體現在哪些方面?
15、Netty的執行緒模型是怎麼樣的?
Reactor執行緒模型
Reactor單執行緒模型 一個NIO執行緒+一個accept執行緒:
Reactor多執行緒模型
Reactor主從模型 主從Reactor多執行緒:多個acceptor的NIO執行緒池用於接受客戶端的連線
Netty可以基於如上三種模型進行靈活的配置。
總結
Netty是建立在NIO基礎之上,Netty在NIO之上又提供了更高層次的抽象。在Netty裡面,Accept連線可以使用單獨的執行緒池去處理,讀寫操作又是另外的執行緒池來處理。Accept連線和讀寫操作也可以使用同一個執行緒池來進行處理。而請求處理邏輯既可以使用單獨的執行緒池進行處理,也可以跟放在讀寫執行緒一塊處理。執行緒池中的每一個執行緒都是NIO執行緒。使用者可以根據實際情況進行組裝,構造出滿足系統需求的高效能併發模型。
16、Netty的零拷貝提體現在哪裡,與作業系統上的有什麼區別?
傳統意義的拷貝是在傳送資料的時候, 傳統的實現方式是:
File.read(bytes)
Socket.send(bytes)
這種方式需要四次資料拷貝和四次上下文切換:
- 資料從磁碟讀取到核心的read buffer
- 資料從核心緩衝區拷貝到使用者緩衝區
- 資料從使用者緩衝區拷貝到核心的socket buffer
- 資料從核心的socket buffer拷貝到網路卡介面(硬體)的緩衝區
零拷貝的概念明顯上面的第二步和第三步是沒有必要的,通過java的FileChannel.transferTo方法,可以避免上面兩次多餘的拷貝(當然這需要底層作業系統支援)
- 呼叫transferTo,資料從檔案由DMA引擎拷貝到核心read buffer
- 接著DMA從核心read buffer將資料拷貝到網路卡介面buffer上面的兩次操作都不需要CPU參與,所以就達到了零拷貝。
Netty中的零拷貝主要體現在三個方面:
1、bytebufferNetty傳送和接收訊息主要使用bytebuffer,bytebuffer使用對外記憶體(DirectMemory)直接進行Socket讀寫。原因:如果使用傳統的堆記憶體進行Socket讀寫,JVM會將堆記憶體buffer拷貝一份到直接記憶體中然後再寫入socket,多了一次緩衝區的記憶體拷貝。DirectMemory中可以直接通過DMA傳送到網路卡介面
2、Composite Buffers傳統的ByteBuffer,如果需要將兩個ByteBuffer中的資料組合到一起,我們需要首先建立一個size=size1+size2大小的新的陣列,然後將兩個陣列中的資料拷貝到新的陣列中。但是使用Netty提供的組合ByteBuf,就可以避免這樣的操作,因為CompositeByteBuf並沒有真正將多個Buffer組合起來,而是儲存了它們的引用,從而避免了資料的拷貝,實現了零拷貝。
3、對於FileChannel.transferTo的使用Netty中使用了FileChannel的transferTo方法,該方法依賴於作業系統實現零拷貝。
17、Netty的記憶體池是怎麼實現的?
netty記憶體池實現原理
netty記憶體池可以分配堆記憶體和非堆記憶體(Direct記憶體),記憶體分配的核心演演算法是類似的,從堆記憶體分配程式碼入手來學習整個記憶體池的原理。netty框架處理IO事件時,使用ByteBuf承載資料。ByteBuf的記憶體分配由PooledByteBufAllocator來執行,最終的記憶體分配工作會被委託給PoolArena,堆記憶體分配的PoolArena實現是HeapArena。
netty通常被用於高併發系統,多執行緒競爭加鎖會影響記憶體分配的效率,為了緩解高併發時的執行緒競爭,netty允許使用者建立多個分配器(PoolArena)來分離執行緒競爭,提高記憶體分配效率。可通過PooledByteBufAllocator構造子中的nHeapArena引數來設定PoolArena的數量,或者直接取框架中的預設值,通過以下程式碼決定預設值,預設值根據CPU核心數、JVM最大可用記憶體以及預設記憶體塊(PoolChunk)大小等引數來計算這個預設值,計算邏輯是:
1)獲取系統變數io.netty.allocator.numHeapArenas,通過System.setProperty("io.netty.allocator.numHeapArenas",xxx)或者增加JVM啟動引數-Dio.netty.allocator.numHeapArenas=xxx設定,如果設定了值,把這個值當做Arena的個數。
2)如果沒有設定io.netty.allocator.numHeapArenas系統變數,計算CPU核心數*2和JVM最大可用記憶體/預設記憶體塊大小/2/3,取其中一個較少的值當做PoolArena的個數。
確定PoolArena個數之後框架會建立一個PoolArena陣列,陣列中所有的PoolArena都會用來執行記憶體分配。執行緒申請記憶體分配時,執行緒會在這個PoolArena陣列中挑選一個當前被佔用次數最少的Arena執行記憶體分配。
此外,netty使用擴充套件的執行緒物件FastThreadLocalThread來優化ThreadLocal效能,具體的優化思路是:預設的ThreadLocal使用ThreadLocalMap儲存執行緒區域性變數,它的實現方式類似於HashMap,需要計算hashCode定位到執行緒區域性變數所在Entry的索引,而FastThreadLocalThread使用FastThreadLocal代替ThreadLocal,FastThreadLocalThread用一個陣列來維護執行緒變數,每個FastThreadLocal維護一個index,該index就是執行緒區域性變數在陣列中的位置,執行緒變數直接通過index訪問無需計算hashCode,FastThreadLocal的優勢是減少了hashCode的計算過程,雖然效能只會有輕微的提升,但在高併發系統中,即使只是輕微的提升也會成倍放大。
更多請閱讀文章:baijiahao.baidu.com/s?id=164049…
18、Netty的物件池是怎麼實現的?
Netty 並沒有使用第三方庫實現物件池,而是自己實現了一個相對輕量的物件池。通過使用 threadLocal,避免了多執行緒下取資料時可能出現的執行緒安全問題,同時,為了實現多執行緒回收同一個例項,讓每個執行緒對應一個佇列,佇列連結在 Stack 物件上形成連結串列,這樣,就解決了多執行緒回收時的安全問題。同時,使用了軟引用的map 和 軟引用的 thradl 也避免了記憶體洩漏。
更詳細的可閱讀文章:
www.jianshu.com/p/834691915… www.cnblogs.com/hzmark/p/ne…
19、在實際專案中,你們是怎麼使用Netty的?
沒用過。
20、使用過Netty遇到過什麼問題?
(1)建立兩個NioEventLoopGroup,用於邏輯隔離NIO Acceptor和NIO I/O執行緒
(2)儘量不要在ChannelHandler中啟動使用者執行緒(解碼後用於將POJO訊息派發到後端業務執行緒的除外)
(3)解碼要放在NIO執行緒呼叫的解碼Handler中進行,不要切換到使用者執行緒完成訊息的解碼.
(4)如果業務邏輯操作非常簡單(純記憶體操作),沒有複雜的業務邏輯計算,也可能會導致執行緒被阻塞的磁碟操作,資料庫操作,網路操作等,可以直接在NIO執行緒上完成業務邏輯編排,不需要切換到使用者執行緒.
(5)如果業務邏輯複雜,不要在NIO執行緒上完成,建議將解碼後的POJO訊息封裝成任務,派發到業務執行緒池中由業務執行緒執行,以保證NIO執行緒儘快釋放,處理其它I/O操作.
(6)可能導致阻塞的操作,第三方服務呼叫,中介軟體服務呼叫,同步獲取鎖,Sleep等
(7)Sharable註解的ChannelHandler要慎用
(8)避免將ChannelHandler加入到不同的ChannelPipeline中,會出現併發問題.
從上面的隨便挑一個吹水就行。
21、netty的執行緒模型,netty如何基於reactor模型上實現的。
這個網上很多了,就不說了。 www.cnblogs.com/coding400/p…
23、netty的fashwheeltimer的用法,實現原理,是否出現過呼叫不夠準時,怎麼解決。
24、netty的心跳處理在弱網下怎麼辦。
25、netty的通訊協議是什麼樣的。