成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),卓尼企業(yè)網(wǎng)站建設(shè),卓尼品牌網(wǎng)站建設(shè),網(wǎng)站定制,卓尼網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,卓尼網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
Netty是一個(gè)高性能、異步事件驅(qū)動(dòng)的NIO框架,它提供了對(duì)TCP、UDP和文件傳輸?shù)闹С郑鳛橐粋€(gè)異步NIO框架,Netty的所有IO操作都是異步非阻塞的,通過(guò)Future-Listener機(jī)制,用戶可以方便的主動(dòng)獲取或者通過(guò)通知機(jī)制獲得IO操作結(jié)果。
作為當(dāng)前最流行的NIO框架,Netty在互聯(lián)網(wǎng)領(lǐng)域、大數(shù)據(jù)分布式計(jì)算領(lǐng)域、游戲行業(yè)、通信行業(yè)等獲得了廣泛的應(yīng)用,一些業(yè)界著名的開(kāi)源組件也基于Netty的NIO框架構(gòu)建。
Netty架構(gòu)分析
Netty 采用了比較典型的三層網(wǎng)絡(luò)架構(gòu)進(jìn)行設(shè)計(jì),邏輯架構(gòu)圖如下所示:
第一層:Reactor 通信調(diào)度層,它由一系列輔助類(lèi)完成,包括 Reactor 線程 NioEventLoop 以及其父類(lèi)、NioSocketChannel/NioServerSocketChannel 以及其父類(lèi)、ByteBuffer 以及由其衍生出來(lái)的各種 Buffer、Unsafe 以及其衍生出的各種內(nèi)部類(lèi)等。該層的主要職責(zé)就是監(jiān)聽(tīng)網(wǎng)絡(luò)的讀寫(xiě)和連接操作,負(fù)責(zé)將網(wǎng)絡(luò)層的數(shù)據(jù)讀取到內(nèi)存緩沖區(qū)中,然后觸發(fā)各種網(wǎng)絡(luò)事件,例如連接創(chuàng)建、連接激活、讀事件、寫(xiě)事件等等,將這些事件觸發(fā)到 PipeLine 中,由 PipeLine 充當(dāng)?shù)穆氊?zé)鏈來(lái)進(jìn)行后續(xù)的處理。
第二層:職責(zé)鏈 PipeLine,它負(fù)責(zé)事件在職責(zé)鏈中的有序傳播,同時(shí)負(fù)責(zé)動(dòng)態(tài)的編排職責(zé)鏈,職責(zé)鏈可以選擇監(jiān)聽(tīng)和處理自己關(guān)心的事件,它可以攔截處理和向后/向前傳播事件,不同的應(yīng)用的 Handler 節(jié)點(diǎn)的功能也不同,通常情況下,往往會(huì)開(kāi)發(fā)編解碼 Hanlder 用于消息的編解碼,它可以將外部的協(xié)議消息轉(zhuǎn)換成內(nèi)部的 POJO 對(duì)象,這樣上層業(yè)務(wù)側(cè)只需要關(guān)心處理業(yè)務(wù)邏輯即可,不需要感知底層的協(xié)議差異和線程模型差異,實(shí)現(xiàn)了架構(gòu)層面的分層隔離。
第三層:業(yè)務(wù)邏輯處理層,可以分為兩類(lèi):
1.純粹的業(yè)務(wù)邏輯處理,例如訂單處理。
2.應(yīng)用層協(xié)議管理,例如HTTP協(xié)議、FTP協(xié)議等。
接下來(lái),我從影響通信性能的三個(gè)方面(I/O模型、線程調(diào)度模型、序列化方式)來(lái)談?wù)凬etty的架構(gòu)。
這里推薦一下我的Java架構(gòu)學(xué)習(xí)群:479499375 ,群里有(Java高架構(gòu)、分布式架構(gòu)、高可擴(kuò)展、高性能、高并發(fā)、性能優(yōu)化、Spring boot、redis、ActiveMQ、等學(xué)習(xí)資源)進(jìn)群免費(fèi)送給每一位Java小伙伴,不管你是轉(zhuǎn)行,還是工作中想提升自己能力都可以!
IO模型
Netty的I/O模型基于非阻塞I/O實(shí)現(xiàn),底層依賴(lài)的是JDK NIO框架的Selector。
Selector提供選擇已經(jīng)就緒的任務(wù)的能力。簡(jiǎn)單來(lái)講,Selector會(huì)不斷地輪詢(xún)注冊(cè)在其上的Channel,如果某個(gè)Channel上面有新的TCP連接接入、讀和寫(xiě)事件,這個(gè)Channel就處于就緒狀態(tài),會(huì)被Selector輪詢(xún)出來(lái),然后通過(guò)SelectionKey可以獲取就緒Channel的集合,進(jìn)行后續(xù)的I/O操作。
線程調(diào)度模型
常用的Reactor線程模型有三種,分別如下:
1.Reactor單線程模型:Reactor單線程模型,指的是所有的I/O操作都在同一個(gè)NIO線程上面完成。對(duì)于一些小容量應(yīng)用場(chǎng)景,可以使用單線程模型。
2.Reactor多線程模型:Rector多線程模型與單線程模型最大的區(qū)別就是有一組NIO線程處理I/O操作。主要用于高并發(fā)、大業(yè)務(wù)量場(chǎng)景。
3.主從Reactor多線程模型:主從Reactor線程模型的特點(diǎn)是服務(wù)端用于接收客戶端連接的不再是個(gè)1個(gè)單獨(dú)的NIO線程,而是一個(gè)獨(dú)立的NIO線程池。利用主從NIO線程模型,可以解決1個(gè)服務(wù)端監(jiān)聽(tīng)線程無(wú)法有效處理所有客戶端連接的性能不足問(wèn)題。
序列化方式
影響序列化性能的關(guān)鍵因素總結(jié)如下:
1.序列化后的碼流大小(網(wǎng)絡(luò)帶寬占用)
2.序列化&反序列化的性能(CPU資源占用)
3.并發(fā)調(diào)用的性能表現(xiàn):穩(wěn)定性、線性增長(zhǎng)、偶現(xiàn)的時(shí)延毛刺等
這里推薦一下我的Java架構(gòu)學(xué)習(xí)群:479499375 ,群里有(Java高架構(gòu)、分布式架構(gòu)、高可擴(kuò)展、高性能、高并發(fā)、性能優(yōu)化、Spring boot、Redis、ActiveMQ、等學(xué)習(xí)資源)進(jìn)群免費(fèi)送給每一位Java小伙伴,不管你是轉(zhuǎn)行,還是工作中想提升自己能力都可以!
鏈路有效性檢測(cè)
心跳檢測(cè)機(jī)制分為三個(gè)層面:
1.TCP層面的心跳檢測(cè),即TCP的Keep-Alive機(jī)制,它的作用域是整個(gè)TCP協(xié)議棧;
2.協(xié)議層的心跳檢測(cè),主要存在于長(zhǎng)連接協(xié)議中。例如SMPP協(xié)議;
3.應(yīng)用層的心跳檢測(cè),它主要由各業(yè)務(wù)產(chǎn)品通過(guò)約定方式定時(shí)給對(duì)方發(fā)送心跳消息實(shí)現(xiàn)。
心跳檢測(cè)的目的就是確認(rèn)當(dāng)前鏈路可用,對(duì)方活著并且能夠正常接收和發(fā)送消息。作為高可靠的NIO框架,Netty也提供了基于鏈路空閑的心跳檢測(cè)機(jī)制:
1.讀空閑,鏈路持續(xù)時(shí)間t沒(méi)有讀取到任何消息;
2.寫(xiě)空閑,鏈路持續(xù)時(shí)間t沒(méi)有發(fā)送任何消息;
3.讀寫(xiě)空閑,鏈路持續(xù)時(shí)間t沒(méi)有接收或者發(fā)送任何消息。
零拷貝
“零拷貝”是指計(jì)算機(jī)操作的過(guò)程中, CPU不需要為數(shù)據(jù)在內(nèi)存之間的拷貝消耗資源 。而它通常是指計(jì)算機(jī)在網(wǎng)絡(luò)上發(fā)送文件時(shí),不需要將文件內(nèi)容拷貝到用戶空間(User Space)而 直接在內(nèi)核空間(Kernel Space)中傳輸?shù)骄W(wǎng)絡(luò)的方式
Netty的“零拷貝”主要體現(xiàn)在三個(gè)方面
Netty的 接收和發(fā)送ByteBuffer采用DIRECT BUFFERS,使用堆外直接內(nèi)存進(jìn)行Socket讀寫(xiě),不需要進(jìn)行字節(jié)緩沖區(qū)的二次拷貝 。如果使用傳統(tǒng)的堆內(nèi)存(HEAP BUFFERS)進(jìn)行Socket讀寫(xiě),JVM會(huì)將堆內(nèi)存Buffer拷貝一份到直接內(nèi)存中,然后才寫(xiě)入Socket中。相比于堆外直接內(nèi)存,消息在發(fā)送過(guò)程中多了一次緩沖區(qū)的內(nèi)存拷貝
讀取直接從“堆外直接內(nèi)存”,不像傳統(tǒng)的堆內(nèi)存和直接內(nèi)存拷貝
ByteBufAllocator 通過(guò)ioBuffer分配堆外內(nèi)存
Netty提供了 組合Buffer對(duì)象 ,可以聚合多個(gè)ByteBuffer對(duì)象,用戶可以 像操作一個(gè)Buffer那樣方便的對(duì)組合Buffer進(jìn)行操作 ,避免了傳統(tǒng)通過(guò)內(nèi)存拷貝的方式將幾個(gè)小Buffer合并成一個(gè)大的Buffer
Netty允許我們將多段數(shù)據(jù)合并為一整段虛擬數(shù)據(jù)供用戶使用,而過(guò)程中不需要對(duì)數(shù)據(jù)進(jìn)行拷貝操作
組合Buffer對(duì)象,避免了內(nèi)存拷貝
ChannelBuffer接口:Netty為需要傳輸?shù)臄?shù)據(jù)制定了統(tǒng)一的ChannelBuffer接口
· 使用getByte(int index)方法來(lái)實(shí)現(xiàn)隨機(jī)訪問(wèn)
· 使用雙指針的方式實(shí)現(xiàn)順序訪問(wèn)
· Netty主要實(shí)現(xiàn)了HeapChannelBuffer,ByteBufferBackedChannelBuffer,與Zero Copy直接相關(guān)的CompositeChannelBuffer類(lèi)
CompositeChannelBuffer類(lèi)
CompositeChannelBuffer類(lèi)的作用是將多個(gè)ChannelBuffer組成一個(gè)虛擬的ChannelBuffer來(lái)進(jìn)行操作
為什么說(shuō)是虛擬的呢,因?yàn)镃ompositeChannelBuffer并沒(méi)有將多個(gè)ChannelBuffer真正的組合起來(lái),而只是保存了他們的引用,這樣就避免了數(shù)據(jù)的拷貝,實(shí)現(xiàn)了Zero Copy,內(nèi)部實(shí)現(xiàn)
其中readerIndex既讀指針和writerIndex既寫(xiě)指針是從AbstractChannelBuffer繼承而來(lái)的
components是一個(gè)ChannelBuffer的數(shù)組,他保存了組成這個(gè)虛擬Buffer的所有子Buffer
indices是一個(gè)int類(lèi)型的數(shù)組,它保存的是各個(gè)Buffer的索引值
lastAccessedComponentId是一個(gè)int值,它記錄了最后一次訪問(wèn)時(shí)的子Buffer ID
CompositeChannelBuffer實(shí)際上就是將一系列的Buffer通過(guò)數(shù)組保存起來(lái),然后實(shí)現(xiàn)了ChannelBuffer 的接口,使得在上層看來(lái),操作這些Buffer就像是操作一個(gè)單獨(dú)的Buffer一樣
Netty的文件傳輸采用了 transferTo方法 ,它可以直接將文件緩沖區(qū)的數(shù)據(jù)發(fā)送到目標(biāo)Channel,避免了傳統(tǒng)通過(guò)循環(huán)write方式導(dǎo)致的內(nèi)存拷貝問(wèn)題
Linux中的sendfile()以及Java NIO中的FileChannel.transferTo()方法都實(shí)現(xiàn)了零拷貝的功能,而在Netty中也通過(guò)在FileRegion中包裝了NIO的FileChannel.transferTo()方法實(shí)現(xiàn)了零拷貝
Netty 的 Zero-copy 體現(xiàn)在如下幾個(gè)個(gè)方面:
l Netty 提供了 CompositeByteBuf 類(lèi), 它可以將多個(gè) ByteBuf 合并為一個(gè)邏輯上的 ByteBuf, 避免了各個(gè) ByteBuf 之間的拷貝。
l 通過(guò) wrap 操作, 我們可以將byte[] 數(shù)組、ByteBuf、ByteBuffer等包裝成一個(gè) Netty ByteBuf 對(duì)象, 進(jìn)而避免了拷貝操作。
l ByteBuf 支持 slice 操作,因此可以將 ByteBuf 分解為多個(gè)共享同一個(gè)存儲(chǔ)區(qū)域的ByteBuf, 避免了內(nèi)存的拷貝。
l 通過(guò) FileRegion 包裝的FileChannel.tranferTo 實(shí)現(xiàn)文件傳輸, 可以直接將文件緩沖區(qū)的數(shù)據(jù)發(fā)送到目標(biāo) Channel, 避免了傳統(tǒng)通過(guò)循環(huán) write 方式導(dǎo)致的內(nèi)存拷貝問(wèn)題。