溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦

發(fā)布時(shí)間:2021-12-28 15:39:59 來源:億速云 閱讀:379 作者:小新 欄目:大數(shù)據(jù)

這篇文章主要為大家展示了“Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦”這篇文章吧。   

正文

導(dǎo)致Netty內(nèi)存泄漏的原因有很多,例如,使用內(nèi)存池創(chuàng)建的對(duì)象忘記釋放,或者對(duì)端系統(tǒng)服務(wù)壓力過大導(dǎo)致發(fā)送隊(duì)列積壓。

盡管Netty采用NIO非阻塞通信,I/O往往不是系統(tǒng)性能的瓶頸,但是如果服務(wù)端處理速度有限,客戶端發(fā)送數(shù)據(jù)量很大,沒有做好流控,同樣會(huì)導(dǎo)致內(nèi)存溢出。

對(duì)某個(gè)業(yè)務(wù)做性能壓測,基于Netty開發(fā)的多個(gè)客戶端并發(fā)鏈接一個(gè)服務(wù)端,客戶端運(yùn)行一段時(shí)間后,內(nèi)存、CPU占用率居高不下,響應(yīng)越來越慢,最后自動(dòng)宕機(jī)。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


為了方便分析,這里簡化代碼,這里用一個(gè)死循環(huán)向服務(wù)端發(fā)送消息,模擬壓測環(huán)境,客戶端代碼如下:

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦                  

Netty發(fā)送數(shù)據(jù)源碼分析

業(yè)務(wù)調(diào)用ChannelHandlerContext.write方法后,經(jīng)過ChannelPipeline責(zé)任鏈處理,消息被投遞到了發(fā)送緩沖區(qū)中待發(fā)送,調(diào)用flush后才真正發(fā)送。

writeAndFlush方法,內(nèi)部調(diào)用的是write方法,代碼如下

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


跟進(jìn)write()方法,處理邏輯如下,首先判斷當(dāng)前線程是否是NioEventLoop,如果不是,將發(fā)送的數(shù)據(jù)封裝成一個(gè)WriteTask,放入NioEventLoop的任務(wù)隊(duì)列由NioEventLoop線程執(zhí)行。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


在execute里做同樣的判斷后,在這里走的是else分支,調(diào)用addTask()后,將任務(wù)添加到任務(wù)隊(duì)列中

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


Netty的NioEventLoop線程內(nèi)部維護(hù)了一個(gè)Queue<Runnable> taskQueue,除了處理網(wǎng)絡(luò)I/O讀寫事件,同時(shí)還負(fù)責(zé)網(wǎng)絡(luò)讀寫相關(guān)的Task。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


經(jīng)過一系列處理后,消費(fèi)端在拿到數(shù)據(jù)后,最終會(huì)調(diào)用ChannelOutboundBuffer的addMessage方法,將消息加入到發(fā)送隊(duì)列。學(xué)過數(shù)據(jù)結(jié)構(gòu)的同學(xué),可以很清楚的看到,這個(gè)發(fā)送隊(duì)列是基于鏈表組織起來的。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


請(qǐng)注意方法結(jié)尾調(diào)用的incrementPendingOutboundByte方法,會(huì)在后面分析。文章開頭描述的現(xiàn)象與此方法有關(guān)。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦                  

如何防止發(fā)送隊(duì)列積壓

為了防止高并發(fā)場景下,由服務(wù)端處理慢導(dǎo)致客戶端消息積壓,除了服務(wù)端做流控外,客戶端也需要做流控,自身保護(hù),方法是,設(shè)置待發(fā)送隊(duì)列的高低水位。

方法有兩種,第一種是在啟動(dòng)類里設(shè)置option屬性

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦

第二種是

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦

當(dāng)發(fā)送隊(duì)列到達(dá)高水位時(shí),對(duì)應(yīng)的Channel就會(huì)變?yōu)椴豢蓪憼顟B(tài)。由于高水位并不影響業(yè)務(wù)線程調(diào)用write方法把消息寫入待發(fā)送隊(duì)列,因此必須在消息發(fā)送時(shí)對(duì)Channel的狀態(tài)進(jìn)行判斷。


為了對(duì)待發(fā)送隊(duì)列發(fā)送速度的控制,Netty提供了高低水位的機(jī)制,當(dāng)積壓消息量達(dá)到高水位時(shí),修改Channel為不可寫狀態(tài),在ChannelOutboundBuffer類

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


修改Channel狀態(tài)后,調(diào)用ChannelPipeline發(fā)送通知消息

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


當(dāng)積壓消息發(fā)送完成后,對(duì)低水位進(jìn)行判斷,如果待發(fā)送字節(jié)數(shù)到達(dá)或低于低水位,修改Channel狀態(tài)為可寫,并發(fā)送通知事件。代碼如下

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


針對(duì)上述分析后,再回頭看文章開頭描述的問題,我們修改代碼為如下格式然后再壓測,允許一段時(shí)間后 ,系統(tǒng)穩(wěn)定。

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦


內(nèi)存消耗如下,可見運(yùn)行非常穩(wěn)定

Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦

以上是“Netty發(fā)送隊(duì)列積壓導(dǎo)致內(nèi)存泄露怎么辦”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI