pos機內存告警

 新聞資訊2  |   2023-07-15 12:10  |  投稿人:pos機之家

網上有很多關于pos機內存告警,長連接Netty服務內存泄漏的知識,也有很多人為大家解答關于pos機內存告警的問題,今天pos機之家(www.shbwcl.net)為大家整理了關于這方面的知識,讓我們一起來看下吧!

本文目錄一覽:

1、pos機內存告警

pos機內存告警

作者:京東科技 王長春

背景

事情要回顧到11.11備戰前夕,在那個風雨交加的夜晚,一個急促的咚咚報警,驚破了電閃雷鳴的黑夜,將沉浸在夢香,熟睡的我驚醒。

一看手機咚咚報警,不好!有大事發生了!電話馬上打給老板:

老板說: 長連接嗎?我說:是的!老板說:該來的還是要來的,最終還是來了,快,趕緊先把服務重啟下!我說:已經重啟了!老板說: 這問題必須給我解決了!我說:必須的!

線上應用長連接Netty服務出現內存泄漏了!真讓人頭大

在這風雨交加的夜晚,此時,面對毫無頭緒的問題,以及迫切想攻克問題的心,已經讓我興奮不已,手一把揉揉剛還迷糊的眼,今晚又注定是一個不眠之夜!

應用介紹

說起支付業務的長連接服務,真是說來話長。

我們還是長話短說——

隨著業務及系統架構的復雜化,一些場景,用戶操作無法同步得到結果。一般采用的短連接輪訓的策略,客戶端需要不停的發起請求,時效性較差還浪費服務器資源。

短輪訓痛點:

時效性差耗費服務器性能建立、關閉鏈接頻繁

相比于短連接輪訓策略,長連接服務可做到實時推送數據,并且在一個鏈接保持期間可進行多次數據推送。服務應用常見場景:PC端掃碼支付,用戶打開掃碼支付頁面,手機掃碼完成支付,頁面實時展示支付成功信息,提供良好的用戶體驗。

長連服務優勢:

時效性高提升用戶體驗減少鏈接建立次數一次鏈接多次推送數據提高系統吞吐量

這個長連接服務使用Netty框架,Netty的高性能為這個應用帶來了無上的榮光,承接了眾多長連接使用場景的業務:

PC收銀臺微信支付聲波紅包POS線下掃碼支付問題現象

回到線上問題,出現內存泄漏的是長連接前置服務,觀察線上服務,這個應用的內存泄漏的現象總伴隨著內存的增長,這個增長真是非常的緩慢,緩慢,緩慢,2、3個月內從30%慢慢增長到70%,極難發現

每次發生內存泄漏,內存快耗盡時,總得重啟下,雖說重啟是最快解決的方法,但是程序員是天生懶惰的,要數著日子來重啟,那絕對不是一個優秀程序員的行為!問題必須徹底解決!

問題排查與復現排查

遇到問題,毫無頭緒,首先還是需要去案發第一現場,排查“死者(應用實例)”死亡現場,通過在發生FullGC的時間點,通過Digger查詢ERROR日志,沒想到還真找到破案的第一線索:

io.netty.util.ResourceLeakDetector [176] - LEAK: ByteBuf.release() was not called before it's garbage-collected. Enable advanced leak reporting to find out where the leak occurred. To enable advanced leak reporting, specify the JVM option '-Dio.netty.leakDetection.level=advanced' or call ResourceLeakDetector.setLevel() See http://netty.io/wiki/reference-counted-objects.html for more information.

線上日志竟然有一個明顯的"LEAK"泄漏字樣,作為技術人的敏銳的技術嗅覺,和找Bug的直覺,可以確認,這就是事故案發第一現場。

我們憑借下大學四六級英文水平的,繼續翻譯下線索,原來是這吶!

ByteBuf.release() 在垃圾回收之前沒有被調用。啟用高級泄漏報告以找出泄漏發生的位置。要啟用高級泄漏報告,請指定 JVM 選項“-Dio.netty.leakDetectionLevel=advanced”或調用 ResourceLeakDetector.setLevel()

啊哈!這信息不就是說了嘛!ByteBuf.release()在垃圾回收前沒有調用,有ByteBuf對象沒有被釋放,ByteBuf可是分配在直接內存的,沒有被釋放,那就意味著堆外內存泄漏,所以內存一直是非常緩慢的增長,GC都不能夠進行釋放。

提供了這個線索,那到底是我們應用中哪段代碼出現了ByteBuf對象的內存泄漏呢?項目這么大,Netty通信處理那么多,怎么找呢?自己從中搜索,那肯定是不靠譜,找到了又怎么釋放呢?

復現

面對這一連三問?別著急,Netty的日志提示還是非常完善:啟用高級泄漏報告找出泄漏發生位置嘛,生產上不可能啟用,并且生產發生時間極長,時間上來不及,而且未經驗證,不能直接生產發布,那就本地代碼復現一下!找到具體代碼位置。

為了本地復現Netty泄漏,定位詳細的內存泄漏代碼,我們需要做這幾步:

1、配置足夠小的本地JVM內存,以便快速模擬堆外內存泄漏。如圖,我們設置設置PermSize=30M, MaxPermSize=43M

2、模擬足夠多的長連接請求,我們使用Postman定時批量發請求,以達到服務的堆外內存泄漏。

啟動項目,通過JProfiler JVM監控工具,我們觀察到內存緩慢的增長,最終觸發了本地Netty的堆外內存泄漏,本地復現成功:

那問題具體出現在代碼中哪塊呢? 我們最重要的是定位具體代碼,在開啟了Netty的高級內存泄漏級別為高級,來定位下:

3、開啟Netty的高級內存泄漏檢測級別,JVM參數如下:-Dio.netty.leakDetectionLevel=advanced

再啟動項目,模擬請求,達到本地應用JVM內存泄漏,Netty輸出如下具體日志信息,可以看到,具體的日志信息比之前的信息更加完善:

2020-09-24 20:11:59.078 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler [101] - [id: 0x2a5e5026, L:/0:0:0:0:0:0:0:0:8883] READ: [id: 0x926e140c, L:/127.0.0.1:8883 - R:/127.0.0.1:58920]2020-09-24 20:11:59.078 [nioEventLoopGroup-3-1] INFO io.netty.handler.logging.LoggingHandler [101] - [id: 0x2a5e5026, L:/0:0:0:0:0:0:0:0:8883] READ COMPLETE2020-09-24 20:11:59.079 [nioEventLoopGroup-2-8] ERROR io.netty.util.ResourceLeakDetector [171] - LEAK: ByteBuf.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.WARNING: 1 leak records were discarded because the leak record count is limited to 4. Use system property io.netty.leakDetection.maxRecords to increase the limit.Recent access records: 5#5:io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.readBytes(AdvancedLeakAwareCompositeByteBuf.java:476)io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.readBytes(AdvancedLeakAwareCompositeByteBuf.java:36)com.jd.jr.keeplive.front.service.nettyServer.handler.LongRotationServerHandler.getClientMassageInfo(LongRotationServerHandler.java:169)com.jd.jr.keeplive.front.service.nettyServer.handler.LongRotationServerHandler.handleHttpFrame(LongRotationServerHandler.java:121)com.jd.jr.keeplive.front.service.nettyServer.handler.LongRotationServerHandler.channelRead(LongRotationServerHandler.java:80)io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)......#4:Hint: 'LongRotationServerHandler#0' will handle the message from this point.io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:1028)io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:36)io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpMessage.touch(HttpObjectAggregator.java:359)......#3:Hint: 'HttpServerExpectContinueHandler#0' will handle the message from this point.io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:1028)io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:36)io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpMessage.touch(HttpObjectAggregator.java:359) ......#2:Hint: 'HttpHeartbeatHandler#0' will handle the message from this point.io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:1028)io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:36)io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpMessage.touch(HttpObjectAggregator.java:359) ......#1:Hint: 'IdleStateHandler#0' will handle the message from this point.io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:1028)io.netty.buffer.AdvancedLeakAwareCompositeByteBuf.touch(AdvancedLeakAwareCompositeByteBuf.java:36)io.netty.handler.codec.http.HttpObjectAggregator$AggregatedFullHttpMessage.touch(HttpObjectAggregator.java:359) ......Created at:io.netty.util.ResourceLeakDetector.track(ResourceLeakDetector.java:237)io.netty.buffer.AbstractByteBufAllocator.compositeDirectBuffer(AbstractByteBufAllocator.java:217)io.netty.buffer.AbstractByteBufAllocator.compositeBuffer(AbstractByteBufAllocator.java:195)io.netty.handler.codec.MessageAggregator.decode(MessageAggregator.java:255) ......

開啟高級的泄漏檢測級別后,通過上面異常日志,我們可以看到內存泄漏的具體地方:com.jd.jr.keeplive.front.service.nettyServer.handler.LongRotationServerHandler.getClientMassageInfo(LongRotationServerHandler.java:169)

不得不說Netty 內存泄漏排查這點是真香!真香好評!

問題解決

找到問題了,那我么就需要解決,如何釋放ByteBuf內存呢?

如何回收泄漏的ByteBuf

其實Netty官方也針對這個問題做了專門的討論,一般的經驗法則是,最后訪問引用計數對象的一方負責銷毀該引用計數對象,具體來說:

如果一個[發送]組件將一個引用計數的對象傳遞給另一個[接收]組件,則發送組件通常不需要銷毀它,而是由接收組件進行銷毀。如果一個組件使用了一個引用計數的對象,并且知道沒有其他對象將再訪問它(即,不會將引用傳遞給另一個組件),則該組件應該銷毀它。

詳情請看翻譯的Netty官方文檔對引用計數的功能使用:

【翻譯】Netty的對象引用計數【原文】Reference counted objects

總結起來主要三個方式方式一:手動釋放,哪里使用了,使用完就手動釋放。方式二:升級ChannelHandler為SimpleChannelHandler, 在SimpleChannelHandler中,Netty對收到的所有消息都調用了ReferenceCountUtil.release(msg)。方式三:如果處理過程中不確定ByteBuf是否應該被釋放,那交給Netty的ReferenceCountUtil.release(msg)來釋放,這個方法會判斷上下文是否可以釋放。

考慮到長連接前置應用使用的是ChannelHandler,如果升級SimpleChannelHandler對現有API接口變動比較大,同時如果手動釋放,不確定是否應該釋放風險也大,因此使用方式三,如下:

線上實例內存正常

問題修復后,線上服務正常,內存使用率也沒有再出現因泄漏而增長,從線上我們增加的日志中看出,FullHttpRequest中ByteBuf內存釋放成功。 從此長連接前置內存泄漏的問題徹底解決

總結

一、Netty的內存泄漏排查其實并不難,Netty提供了比較完整的排查內存泄漏工具

JVM 選項 -Dio.netty.leakDetection.level

目前有 4 個泄漏檢測級別的:

DISABLED - 完全禁用泄漏檢測。不推薦。SIMPLE - 抽樣 1% 的緩沖區是否有泄漏。默認。ADVANCED - 抽樣 1% 的緩沖區是否泄漏,以及能定位到緩沖區泄漏的代碼位置。PARANOID - 與 ADVANCED 相同,只是它適用于每個緩沖區,適用于自動化測試階段。如果生成輸出包含“LEAK:”,則可能會使生成失敗。

本次內存泄漏問題,我們通過本地設置泄漏檢測級別為高級,即:-Dio.netty.leakDetectionLevel=advanced定位到了具體內存泄漏的代碼。

同時Netty也給出了避免泄漏的最佳實踐

在 PARANOID 泄漏檢測級別以及 SIMPLE 級別運行單元測試和集成測試。在 SIMPLE 級別向整個集群推出應用程序之前,請先在相當長的時間內查看是否存在泄漏。如果有泄漏,灰度發布中使用 ADVANCED 級別,以獲得有關泄漏來源的一些提示。不要將泄漏的應用程序部署到整個群集。

二、解決Netty內存泄漏,Netty也提供了指導方案,主要有三種方式

方式一:手動釋放,哪里使用了,使用完就手動釋放,這個對使用方要求比較高了方式二:如果處理過程中不確定ByteBuf是否應該被釋放,那交給Netty的ReferenceCountUtil.release(msg)來釋放,這個方法會判斷上下文中是否可以釋放,簡單方便方式三:升級ChannelHandler為SimpleChannelHandler, 在SimpleChannelHandler中,Netty對收到的所有消息都調用了ReferenceCountUtil.release(msg), 升級接口,可能對現有API改動會比較大

以上就是關于pos機內存告警,長連接Netty服務內存泄漏的知識,后面我們會繼續為大家整理關于pos機內存告警的知識,希望能夠幫助到大家!

轉發請帶上網址:http://www.shbwcl.net/newsone/84981.html

你可能會喜歡:

版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 babsan@163.com 舉報,一經查實,本站將立刻刪除。