線程池調(diào)用kafka發(fā)送消息產(chǎn)生的內(nèi)存泄漏問題排查解決
布控預警的需求
在布控預警的需求實現(xiàn)里,我需要把持久化在數(shù)據(jù)庫中的布控對象(身份證、姓名、手機號、imsi、faceId等等都可分別作為布控對象)一直往kafka里發(fā)送,然后由flink進行消費,把當前的布控對象和存儲的用戶軌跡記錄(有旅客、航班、車輛卡口、人臉等等數(shù)據(jù))同時包含身份證、姓名等等信息進行比對,如果比對成功則觸發(fā)布控預警
最開始我是通過單線程發(fā)送,發(fā)現(xiàn)發(fā)送速度有點慢,這里面我需要把布控對象做一些處理(比如split,flat,fitler)后才發(fā)送到kafka,大概30w+的布控對象需要十多分鐘那邊才能預警到,于是開始進行性能優(yōu)化,使用并行流進行處理,改變之后同樣30w的數(shù)據(jù)這次只需要8秒左右就能發(fā)送完成,然而跑了一陣之后程序開始出現(xiàn)報錯
Received invalid metadata error in produce request on partition xxxTopic due to org.apache.kafka.common.errors.
NetworkException: The server disconnected before a response was received.. Going to request metadata update now
報錯分析
第一眼看到這個錯,我以為是kafka或者網(wǎng)絡出問題了,也去看了broker的日志,發(fā)現(xiàn)是有一些關于當前topic被刪除的錯誤,但是日志級別只是一個info,我以為是topic出現(xiàn)錯誤,又嘗試了使用kakfa的消費命令,發(fā)現(xiàn)是能夠正常消費的,說明這個topic和metadata應該是沒有問題的。
我也在網(wǎng)上搜索著,發(fā)現(xiàn)很少有這個問題的說明,但是我發(fā)現(xiàn)了一些特征,經(jīng)過我重啟后程序又是可以繼續(xù)發(fā)送的,而且速度還是很快,但是跑了一會兒,開始又有這個報錯了,開始是一個報錯,其他的都成功,后來是慢慢的,報錯越來越多,成功的越來越少。接著過了很久竟然還發(fā)生了nacos的心跳超時導致服務不可用的情況。
我這時想到了之前看過的一些文章,說頻繁fullgc可能會導致心跳請求失敗的問題,最開始的時候看了cpu使用率,發(fā)現(xiàn)非常高,600%左右,我以為是因為我的程序里面因為是有定時任務去循環(huán)發(fā)送消息所有有點占用是正常的,沒有當回事,后來我用起了arthas和gc命令,先使用dashboard,看了下cpu線程,發(fā)現(xiàn)gc線程占用以及那個并行流forkjoinpool的線程占用非常大的cpu利用率,然后full gc次數(shù)非常多,又使用
jstat -gcutil $pid 1000
命令進行確認,開始我就發(fā)現(xiàn)了old區(qū)的占用比非常高,且fullgc的頻率非常高,幾乎是幾秒鐘就有一次,最重要的是,最開始是能回收一些內(nèi)存,隨著時間的推移,old區(qū)占用比基線一直在增長,最后到了100,在這個過程中我也發(fā)現(xiàn)了,出現(xiàn)報錯的頻率和old區(qū)占用比之間是存在關系的,報錯越來越多的時候,old區(qū)被占用的越多,fullgc越頻繁。
內(nèi)存泄漏
這里我才意識到這是出現(xiàn)內(nèi)存泄漏的問題了吧,可能是我寫的代碼有點問題,于是我繼續(xù)開始排查,通過
jmap dump:format=b,file=xx.hprof $pid
然后導出文件到本地,用java visualvm打開(也可以用mat或者idea的profiler進行解析,我這里用visualvm解析類實例的還報了堆內(nèi)存不足,要改一下/lib/visualvm/etc/visualvm.conf,把-xms改大點)
查看了一下占用最大的幾個對象ObjectiveVO(這個是我推送到kafka的封裝對象)、LogUtil.$$(這個是我發(fā)送消息的工具類)、java.util.concurrent.LinkedBlockingDeque$NODE,還有char[]、String這幾個占用是比較大的,看到這里我逐漸清晰了,在logUtil里面的代碼我是從其他需要發(fā)送日志的地方拷過來的,在這里我加了一個異步executor的操作,主要就是為了隔離業(yè)務和日志的發(fā)送或者發(fā)生異常的情況,然而,我把這個executor的線程池隊列設置為了new LinkedBlockingDeque<>(),這個的隊列的默認長度值為Integer.MAX,因為我們的系統(tǒng)屬于to G系統(tǒng),所以操作日志是比較少的,一直沒有發(fā)現(xiàn)這個問題,而在這個布控發(fā)送Kafka的場景中,數(shù)據(jù)量非常大,8秒鐘左右差不多就是30w的數(shù)據(jù),為了快速實現(xiàn)布控我甚至在外層又加了一個并行流,然后這里logUtil里面又有一個異步把ObjectiveVO加入到隊列,所以才導致的內(nèi)存溢出了....
后面我把這個異步executor去掉了,就沒有發(fā)生內(nèi)存溢出的問題,也就沒有發(fā)生上面報的NetworkException,或者如果要使用線程池異步的話,一定要設計一下隊列的長度,以及拒絕策略?。?!
以上就是線程池調(diào)用kafka發(fā)送消息產(chǎn)生的內(nèi)存泄漏問題的詳細內(nèi)容,更多關于kafka發(fā)送消息內(nèi)存泄漏的資料請關注腳本之家其它相關文章!
相關文章
解讀@ResponseBody與@RequestBody注解的用法
這篇文章主要介紹了Spring MVC中的@ResponseBody和@RequestBody注解的用法,@ResponseBody注解用于將Controller方法的返回對象轉換為指定格式(如JSON)并通過Response響應給客戶端,@RequestBody注解用于讀取HTTP請求的內(nèi)容2024-11-11
Spring中的@ControllerAdvice三種用法詳解
這篇文章主要介紹了Spring中的@ControllerAdvice三種用法詳解,加了@ControllerAdvice的類為那些聲明了(@ExceptionHandler、@InitBinder或@ModelAttribute注解修飾的)方法的類而提供的<BR>專業(yè)化的@Component,以供多個Controller類所共享,需要的朋友可以參考下2024-01-01
詳解Java8新特性之interface中的static方法和default方法
這篇文章主要介紹了Java8新特性之interface中的static方法和default方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08
淺析Spring配置中的classpath:與classpath*:的區(qū)別
這篇文章主要介紹了Spring配置中的"classpath:"與"classpath*:"的區(qū)別,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
IntelliJ IDEA 老司機居然還沒用過 Stream Trace功能(問題小結)
很多朋友酷愛Java8 Stream功能,但是在使用過程中總不是那么順利,下面通過本文給大家分享idea Stream Trace調(diào)試過程遇到的問題,需要的朋友參考下吧2021-05-05
Java Web監(jiān)聽器如何實現(xiàn)定時發(fā)送郵件
這篇文章主要介紹了Java Web監(jiān)聽器如何實現(xiàn)定時發(fā)送郵件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-12-12

