基礎
-
TCP和UDP的區別?
- TCP是面向連接的(在客戶端和服務器之間傳輸數據之前要先建立連接),UDP是無連接的(發送數據之前不需要先建立連接)
- TCP提供可靠的服務(通過TCP傳輸的數據。無差錯,不丟失,不重復,且按序到達);UDP提供面向事務的簡單的不可靠的傳輸。
- UDP具有較好的實時性,工作效率比TCP高,適用於對高速傳輸和實時性比較高的通訊或廣播通信。隨着網速的提高,UDP使用越來越多。
- 沒一條TCP連接只能是點到點的,UDP支持一對一,一對多和多對多的交互通信。
- TCP對系統資源要去比較多,UDP對系統資源要求比較少
- UDP程序結構更加簡單
- TCP是流模式,UDP是數據報模式
-
TCP協議如何保證可靠傳輸?
TCP通過序列號、檢驗和、確認應答信號、重發控制、連接管理、窗口控制、流量控制、擁塞控制實現可靠性。
參考:https://www.jianshu.com/p/6aac4b2a9fd7 -
TCP的握手、揮手機制?
參考:https://blog.csdn.net/zengrenyuan/article/details/80313449 -
TCP的粘包/拆包原因及其解決方法是什么?
TCP是基於字節流的,雖然應用層和TCP傳輸層之間的數據交互是大小不等的數據塊,但是TCP把這些數據塊僅僅看成一連串無結構的字節流,沒有邊界。另外從TCP的幀結構也可以看出,在TCP的首部沒有表示數據長度的字段。常見的發生原因:- 要發送的數據大於TCP發送緩沖區剩余空間大小,將會發生拆包。
- 待發送數據大於MSS(最大報文長度),TCP在傳輸前將進行拆包。
- 要發送的數據小於TCP發送緩沖區的大小,TCP將多次寫入緩沖區的數據一次發送出去,將會發生粘包。
- 接收數據端的應用層沒有及時讀取接收緩沖區中的數據,將發生粘包。
解決問題的關鍵在於如何給每個數據包添加邊界信息,常用的方法如下:
- 發送端給每個數據包添加包首部,首部中應該至少包含數據包的長度,這樣接收端在接收到數據后,通過讀取包首部的長度字段,便知道每一個數據包的實際長度了。
- 發送端將每個數據包封裝為固定長度(不夠的可以通過補0填充),這樣接收端每次從接收緩沖區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。
- 可以在數據包之間設置邊界,如添加特殊符號,這樣,接收端通過這個邊界就可以將不同的數據包拆分開。
-
Netty的粘包/拆包是怎么處理的,有哪些實現?
- 消息定長,報文大小固定長度,不夠空格補全,發送和接收方遵循相同的約定,這樣即使粘包了通過接收方編程實現獲取定長報文也能區分。
- 包尾添加特殊分隔符,例如每條報文結束都添加回車換行符(例如FTP協議)或者指定特殊字符作為報文分隔符,接收方通過特殊分隔符切分報文區分。
- 將消息分為消息頭和消息體,消息頭中包含表示信息的總長度(或者消息體長度)的字段
參考:https://www.cnblogs.com/sidesky/p/6913109.html
-
同步與異步、阻塞與非阻塞的區別?
-
同步:發出一個功能調用時,在沒有得到結果之前,該調用就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事。例如普通B/S模式(同步):提交請求->等待服務器處理->處理完畢返回 這個期間客戶端瀏覽器不能干任何事。
-
異步:當一個異步過程調用發出后,調用者不能立刻得到結果。實際處理這個調用的部件在完成后,通過狀態、通知和回調來通知調用者。例如 ajax請求(異步): 請求通過事件觸發->服務器處理(這是瀏覽器仍然可以作其他事情)->處理完畢
-
阻塞:阻塞調用是指調用結果返回之前,當前線程會被掛起(線程進入非可執行狀態,在這個狀態下,cpu不會給線程分配時間片,即線程暫停運行)。函數只有在得到結果之后才會返回。有人也許會把阻塞調用和同步調用等同起來,實際上他是不同的。對於同步調用來說,很多時候當前線程還是激活的,只是從邏輯上當前函數沒有返回,它還會搶占cpu去執行其他邏輯,也會主動檢測io是否准備好。
-
非阻塞:指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回。
-
-
說說網絡IO模型?
網絡IO的本質是socket的讀取,socket在linux系統被抽象為流,IO可以理解為對流的操作。- 阻塞IO(bloking IO)
- 非阻塞IO(non-blocking IO)
- 多路復用IO(multiplexing IO)
- 信號驅動式IO(signal-driven IO)
- 異步IO(asynchronous IO)
參考:https://www.jianshu.com/p/a95bcb116765
-
BIO. NIO. AIO分別是什么?
- BIO:同步並阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,當然可以通過線程池機制改善。BIO方式適用於連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,並發局限於應用中,JDK1.4以前的唯一選擇,但程序直觀簡單易理解。
- NIO:同步非阻塞,服務器實現模式為一個請求一個線程,即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有I/O請求時才啟動一個線程進行處理。NIO方式適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,並發局限於應用中,編程比較復雜,JDK1.4開始支持。
- AIO:異步非阻塞,服務器實現模式為一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啟動線程進行處理.AIO方式使用於連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用OS參與並發操作,編程比較復雜,JDK7開始支持。
-
select. poll. epoll的機制及其區別?
參考:https://www.cnblogs.com/aspirant/p/9166944.html -
說說你對Netty的了解?
-
Netty跟Java NIO有什么不同,為什么不直接使用JDK NIO類庫?
- NIO的類庫和API繁雜,使用麻煩,你需要熟練掌握Selector,ServerSocketChannel、SocketChannel、ByteBuffer等。
- 需要具備其他的額外技能做鋪墊,例如熟悉Java多線程編程。這是因為NIO編程涉及到Reactor模式,你必須對多線程和網絡編程非常熟悉,才能寫出高質量的NIO程序。
- 可靠性能力補齊,工作量和難度非常大。例如客戶端面臨斷連重連、網絡閃斷、半包讀寫、失敗緩存、網絡擁塞和異常碼流的處理等問題,NIO編程的特點是功能開發相對容易,但是可靠性能力補齊的工作量和難度都非常大。
- JDK NIO的BUG,例如epoll bug,它會導致Selector空輪詢,最終導致CPU 100%。官方驗證例子基於以上原因,在大多數場景下,不建議直接使用JDK的NIO類庫,除非你精通NIO編程或者有特殊的需求。在絕大多數的業務場景中,我們可以使用NIO框架Netty來進行NIO編程,它既可以作為客戶端也可以作為服務端,同時支持UDP和異步文件傳輸,功能非常強大。
Netty特性總結如下: - API使用簡單,開發門檻低
- 功能強大,預置了多種編解碼功能,支持多種主流協議
- 定制能力強,可以通過ChannelHandler對通信框架進行靈活地擴展
- 性能高,通過與其他業界主流的NIO框架對比,Netty的綜合性能最優
- 成熟、穩定,Netty修復了已經發現的所有JDK NIO BUG,業務開發人員不需要再為NIO的BUG而煩惱
- 社區活躍,版本迭代周期短,發現的BUG可以被及時修復,同時更多的新功能會加入
- 經歷了大規模的商業應用考驗,質量得到驗證。
參考:https://www.520mwx.com/view/20451
-
Netty組件有哪些,分別有什么關聯?
-
Channel
基礎的IO操作,如綁定、連接、讀寫等都依賴於底層網絡傳輸所提供的原語,在Java的網絡編程中,基礎核心類是Socket,而Netty的Channel提供了一組API,極大地簡化了直接與Socket進行操作的復雜性,並且Channel是很多類的父類,如EmbeddedChannel、LocalServerChannel、NioDatagramChannel、NioSctpChannel、NioSocketChannel等。 -
EventLoop
EventLoop定義了處理在連接過程中發生的事件的核心抽象,之后會進一步討論,其中Channel、EventLoop、Thread和EventLoopGroup之間的關系如下圖所示:
-
ChannelHandler和ChannelPipeline
從應用開發者看來,ChannelHandler是最重要的組件,其中存放用來處理進站和出站數據的用戶邏輯。ChannelHandler的方法被網絡事件觸發,ChannelHandler可以用於幾乎任何類型的操作,如將數據從一種格式轉換為另一種格式或處理拋出的異常。例如,其子接口ChannelInboundHandler,接受進站的事件和數據以便被用戶定義的邏輯處理,或者當響應所連接的客戶端時刷新ChannelInboundHandler的數據。
ChannelPipeline為ChannelHandler鏈提供了一個容器並定義了用於沿着鏈傳播入站和出站事件流的API。當創建Channel時,會自動創建一個附屬的ChannelPipeline。 -
Bootstrap 和 ServerBootstrap
Netty的引導類應用程序網絡層配置提供容器,其涉及將進程綁定到給定端口或連接一個進程到在指定主機上指定端口上運行的另一進程。引導類分為客戶端引導Bootstrap和服務端引導ServerBootstrap。
-
參考:https://www.cnblogs.com/leesf456/p/6831661.html
- 說說Netty的執行流程?
- 創建ServerBootStrap實例
- 設置並綁定Reactor線程池:EventLoopGroup,EventLoop就是處理所有注冊到本線程的Selector上面的Channel
- 設置並綁定服務端的channel
- 創建處理網絡事件的ChannelPipeline和handler,網絡時間以流的形式在其中流轉,handler完成多數的功能定制:比如編解碼 SSl安全認證
- 綁定並啟動監聽端口
- 當輪訓到准備就緒的channel后,由Reactor線程:NioEventLoop執行pipline中的方法,最終調度並執行channelHandler
高級
-
Netty高性能體現在哪些方面?
- 傳輸:IO模型在很大程度上決定了框架的性能,相比於bio,netty建議采用異步通信模式,因為nio一個線程可以並發處理N個客戶端連接和讀寫操作,這從根本上解決了傳統同步阻塞IO一連接一線程模型,架構的性能、彈性伸縮能力和可靠性都得到了極大的提升。正如代碼中所示,使用的是NioEventLoopGroup和NioSocketChannel來提升傳輸效率。
- 協議:采用什么樣的通信協議,對系統的性能極其重要,netty默認提供了對Google Protobuf的支持,也可以通過擴展Netty的編解碼接口,用戶可以實現其它的高性能序列化框架。
- 線程:netty使用了Reactor線程模型,但Reactor模型不同,對性能的影響也非常大,下面介紹常用的Reactor線程模型有三種,分別如下:
- Reactor單線程模型:單線程模型的線程即作為NIO服務端接收客戶端的TCP連接,又作為NIO客戶端向服務端發起TCP連接,即讀取通信對端的請求或者應答消息,又向通信對端發送消息請求或者應答消息。理論上一個線程可以獨立處理所有IO相關的操作,但一個NIO線程同時處理成百上千的鏈路,性能上無法支撐,即便NIO線程的CPU負荷達到100%,也無法滿足海量消息的編碼、解碼、讀取和發送,又因為當NIO線程負載過重之后,處理速度將變慢,這會導致大量客戶端連接超時,超時之后往往會進行重發,這更加重了NIO線程的負載,最終會導致大量消息積壓和處理超時,NIO線程會成為系統的性能瓶頸。
- Reactor多線程模型:有專門一個NIO線程用於監聽服務端,接收客戶端的TCP連接請求;網絡IO操作(讀寫)由一個NIO線程池負責,線程池可以采用標准的JDK線程池實現。但百萬客戶端並發連接時,一個nio線程用來監聽和接受明顯不夠,因此有了主從多線程模型。
- 主從Reactor多線程模型:利用主從NIO線程模型,可以解決1個服務端監聽線程無法有效處理所有客戶端連接的性能不足問題,即把監聽服務端,接收客戶端的TCP連接請求分給一個線程池。因此,在代碼中可以看到,我們在server端選擇的就是這種方式,並且也推薦使用該線程模型。在啟動類中創建不同的EventLoopGroup實例並通過適當的參數配置,就可以支持上述三種Reactor線程模型。
-
Netty的線程模型是怎么樣的?
參考:https://segmentfault.com/a/1190000015484187#articleHeader2 -
Netty的零拷貝提體現在哪里,與操作系統上的有什么區別?
Zero-copy就是在操作數據時, 不需要將數據buffer從一個內存區域拷貝到另一個內存區域。 少了一次內存的拷貝,CPU的效率就得到的提升。在OS層面上的Zero-copy 通常指避免在 用戶態(User-space) 與 內核態(Kernel-space)之間來回拷貝數據。Netty的Zero-copy完全是在用戶態(Java 層面)的, 更多的偏向於優化數據操作。- Netty的接收和發送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內存進行Socket讀寫,不需要進行字節緩沖區的二次拷貝。如果使用傳統的堆內存(HEAP BUFFERS)進行Socket讀寫,JVM會將堆內存Buffer拷貝一份到直接內存中,然后才寫入Socket中。相比於堆外直接內存,消息在發送過程中多了一次緩沖區的內存拷貝。
- Netty提供了組合Buffer對象,可以聚合多個ByteBuffer對象,用戶可以像操作一個Buffer那樣方便的對組合Buffer進行操作,避免了傳統通過內存拷貝的方式將幾個小Buffer合並成一個大的Buffer。
- Netty的文件傳輸采用了transferTo方法,它可以直接將文件緩沖區的數據發送到目標Channel,避免了傳統通過循環write方式導致的內存拷貝問題。
參考:https://www.jianshu.com/p/a199ca28e80d
-
Netty的內存池是怎么實現的?
https://blog.csdn.net/kuangzhanshatian/article/details/80881904
https://www.jianshu.com/p/8d894e42b6e6 -
Netty的對象池是怎么實現的?
參考:https://www.jianshu.com/p/3bfe0de2b022