java使用netty的模型總結


由於本人的碼雲太多太亂了,於是決定一個一個的整合到一個springboot項目里面。

附上自己的github項目地址 https://github.com/247292980/spring-boot

附上匯總博文地址 https://www.cnblogs.com/ydymz/p/9391653.html

 

以整合功能

spring-boot,FusionChart,thymeleaf,vue,ShardingJdbc,mybatis-generator,微信分享授權,drools,spring-security,spring-jpa,webjars,Aspect,drools-drt,rabbitmq,zookeeper,mongodb,mysql存儲過程,前端的延遲加載,netty,postgresql,樹的遍歷

 

這次再次說下netty,為什么在學netty呢?因為面試一家大公司的時候,被問倒了,,,所以只好耐下心來看原理了。

目前看了兩本netty的書(市面上就兩本 orz)第一感覺就是netty的書怎么說呢,就是demo集合,某程度還是挺好看懂的,但是過時啊。

不過參考之前寫drools的痛苦經驗,國內大部分環境應該也是用過時的netty,不信你去技術群里問下新版netty的特性,大家一定會勸你去踩坑...

 

而這篇文章也不介紹新特性,只是對netty的原理研究。說實話就是丟圖,,,

 

二 模型

說到netty一定要知道他的基本模型。

1.先說下之前的bio-同步阻塞io

顯然有點網絡基礎的人都寫過類似的代碼。優缺點很明顯

缺點:性能瓶頸。每個鏈接一個線程,怎么保證線程的即時回收和可靠回收是一大技術問題。而該模型在遇到大量線程的時候(超過某個閾值,例如java默認的線程上限或者linux的默認線程上限之類的)性能指數性下降啊。

優點:簡單。所以我才說有點網絡基礎的人都寫過類似的代碼,,,除非直接起手springboot的

 

2.后面就是nio-同步非阻塞io

acceptor收到所有的連接請求

acceptor上注冊了selector,

服務器構建對應的Channel,並在其上注冊Selector

selector分配請求到不同的channel,進行相應的讀寫處理。

 

ps.寫完發現可能有點難理解,具體代碼項目里面有。

其實就是selector分配請求,channel處理請求並能通過selector得到client,等處理完成后直接返回client,不走selector。

 

優點:一個線程處理全部連接,不阻塞后面的連接。性能大提升!

缺點:模型復雜,代碼復雜(至少我覺得不看一下代碼,很難說清);處理半包問題

 

ps.半包問題

我們知道TCP/IP在發送消息的時候,可能會拆包。這就導致接收端無法知道什么時候收到的數據是一個完整的數據。

例如:發送端分別發送了ABC,DEF,GHI三條信息,發送時被拆成了AB,CDRFG,H,I這四個包進行發送,接受端如何將其進行還原呢?在BIO模型中,當讀不到數據后會阻塞,而NIO中不會!所以需要自行進行處理!說到底就是自定義網絡協議,額其實這個有點基礎的都寫過,我也不細說了。

 

3.所以為了解決這問題,netty就使用了Reactor模型-bio的變種

具體來說這是reactor的單線程模型,可以看出和bio基本沒什么差別,就是把channel的數據處理提到handler,並且統一在reactor注冊了,而不用去acceptor,channel分別注冊在兩個地方,統一處理了。

但是這玩意有個問題就是,一個client多次請求,handler中的處理特別慢,那么后續的請求就會被積壓。

 

4.Reactor多線程模型

 

reactor將接受發送分離,client發送的請求丟到線程池中,所以后續請求不會被阻塞。

但是當用戶進一步增加的時候,Reactor會出現瓶頸!因為Reactor既要處理IO操作請求,又要響應連接請求!為了分擔Reactor的負擔,所以引入了主從Reactor模型!

上面的這一句話是書上原話,其實我是不太贊成的,因為說到底就是一個線程的事,所以沒可能是性能瓶頸,而在我看來netty使用主從reactor的主要原因是代碼可讀性和易於理解。

5.Reactor主從模型

簡單明了

client發送請求

acceptor就是個死循環監聽

mainreactor分配acceptor監聽到的請求到channel

channel通過subreactor將請求發到handler處理,然后直接返回給client

 

ps.注意的是mainreactor一定是單線程,多線程可能導致為client分配channel的時候,一個線程分配了一個,浪費資源;也存在一個client的請求去了兩線程,而其中一個處理很久,另一個則早早完成,而此時client的等待時間是取最大等待時間的,那么早早完成的還不如一起在另一個處理很久的線程里面;

 

主從模型最精彩的地方在於,主reactor和從reactor的分離,可以讓住reactor有更多的花樣,如驗權授權等,更加便於大數據手機分析

 

三 術語

bootstrap: Netty 通過設置 bootstrap(引導)類的開始,該類提供了一個應用程序網絡層配置的容器

 

Channel: 是一個客戶端用來進行連接的 Channel,記錄了client信息,用於io操作的交互

ChannelHandler: 數據處理的容器

ChannelPipeline:提供了一個容器給 ChannelHandler 鏈並提供了一個API 用於管理沿着鏈入站和出站事件的流動

 

EventLoop: EventLoop 用於處理 Channel 的 I/O 操作。一個單一的 EventLoop通常會處理多個 Channel 事件

EventLoopGroup:可以含有多於一個的 EventLoop 和 提供了一種迭代用於檢索清單中的下一個

 

ChannelFuture: ChannelFuture是netty的一個回調不管是否成功返回

ChannelFutureListener:ChannelFuture通過addListener 注冊。當操作完成時,可以被通知(不管成功與否)

 

ps.ChannelFutureListener加上了之后就可以說是實現異步。

 

四 channel

通常來說, 所有的 netty 的 I/O 操作都是從 Channel 開始的. 一個 channel 類似於一個 stream.

 

Stream 和 Channel 對比

我們可以在同一個 Channel 中執行讀和寫操作, 然而同一個 Stream 僅僅支持讀或寫.

Channel 可以異步地讀寫, 而 Stream 是阻塞的同步讀寫.

Channel 總是從 Buffer 中讀取數據, 或將數據寫入到 Buffer 中.

 

Channel 類型有:

FileChannel, 文件操作

DatagramChannel, UDP 操作

SocketChannel, TCP 操作

ServerSocketChannel, TCP 操作, 使用在服務器端.

五 Buffer

與 Channel 進行交互時, 我們就需要使用到 Buffer, 即數據從 Buffer讀取到 Channel 中, 並且從 Channel 中寫入到 Buffer 中.

Buffer 其實就是一塊內存區域,  並提供了一些操作方法讓我們能夠方便地進行數據的讀寫.


Buffer 類型有:

ByteBuffer,CharBuffer,DoubleBuffer,FloatBuffer,IntBuffer,LongBuffer,ShortBuffer(就是基本類型啊)

 

六 Selector

Selector 允許一個單一的線程來操作多個 Channel. 如果我們的應用程序中使用了多個 Channel, 那么使用 Selector 很方便的實現這樣的目的, 但是因為在一個線程中使用了多個 Channel, 因此也會造成了每個 Channel 傳輸效率的降低.

為了使用 Selector, 我們首先需要將 Channel 注冊到 Selector 中, 隨后調用 Selector 的 select()方法, 這個方法會阻塞, 直到注冊在 Selector 中的 Channel 發送可讀寫事件. 當這個方法返回后, 當前的這個線程就可以處理 Channel 的事件了.

 

七 Bootstrap

Bootstrap 是 Netty 提供的一個便利的工廠類, 我們可以通過它來完成 Netty 的客戶端或服務器端的 Netty 初始化.

 

八 ChannelPipeline

一個 Channel 包含了一個 ChannelPipeline, 而 ChannelPipeline 中又維護了一個由 ChannelHandlerContext 組成的雙向鏈表

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM