APNS開源包的內存泄露問題

APNS(全稱:Apple Push Notification Service),主要是用於往蘋果設備推送push消息通知!


基本流程:



今天要聊的問題集中在第4個環節,我們自己的服務器往蘋果的消息中心推送通知。


現狀:

歷史原因,push的代碼散落在各個應用中,隨着新消息通道不斷接入,開發、維護成本較高,開始考慮構建push中心,

封裝dubbo接口對外提供服務,對外屏蔽各種差異,將所有的push業務逐步收擾到push中心。

過程漫長,開始接入的是個人業務,每天的調用量不大,服務器還表現正常;

8月底,BI的推送管理後臺開始對接進來併發布上線,由於BI是針對各種營銷活動批量推送的,一次任務少則幾萬,多則上千萬,

此時服務器開始暴露一些問題。


下面開始介紹優化過程:


1)第一次線上問題暴露,現象:短信報警,查看dubbo註冊中心無provider服務,登錄線上機器,load飆高到了40,ps看了jvm進程在,但dubbo日誌裏,服務註冊失敗。

查看gc,一直觸發Full gc,old空間卻釋放不了


解決:重啓集羣。之前的策略是發送過來的數據會封裝到一個線程任務裏,由ThreadPoolExecutor慢慢消化,懷疑一次發送的token過多(一次400),生產速度快於消費速度,導致對象積累。聯繫BI的稀土同學,將一次任務的數量調整爲100,並且每次調用接口後休眠100ms。


2)另外查看了jvm的參數,修改啓動腳本,將原來的堆大小由1G調整爲2G,新生代由原來的300M調整爲1G

-Xms2g -Xmx2g -Xmn1g -XX:+UseParallelOldGC


瞭解BI的機器配置,24核cpu 64G內存,配置很高但只有一臺,採用的是dubbo默認的隨機路由方式,1對多,擔心負載不均衡,

注:線上dubbo註冊中心觀察過,並不是所有的機器同時宕機,而是一個逐步的過程

調整路由策略,改爲輪詢方式:

更多內容可以參考 dubbo的開發手冊

<dubbo:reference id="***" interface="******" loadbalance="roundrobin" />


3)這次持續的時間長了點,不過在任務跑了4個小時後,系統的old區佔用到70%多,開始擔心一會Full GC是否會正常回收。

由於採用的是UseParallelOldGC 並行回收方式(適用於吞吐量大應用類型),不象CMS可以設置空間使用比例主動觸發回收。

但我們可以通過dump內存快照方式手動觸發一次Full GC

jmap -dump:live,format=b,file=heap.bin <pid>




很不幸,Full GC 清理一次後 old區依然有 68%+,可以肯定,發生內存泄露

開始安裝mat插件,分析內存快照,具體可參考《MAT使用教程

發現有大量的SSLSocketImpl實例對象無法回收,整個鏈路佔了heap 50%+


4) 這個問題比較棘手,因爲我們使用的是一個外部開源框架;

只能網上先查查資料,看看有沒有其他人遇到過類似問題;

很不幸沒有找到現成答案,幸運的是在github上找到了源代碼。

https://github.com/fernandospr/javapns-jdk16

不過也只有代碼,並沒有過多文檔介紹,不過這不重要

自己動手,豐衣足食。


分析代碼,發現兩處疑點:

a)NotificationThreads 裏面會根據預傳的線程數量,創建n個線程,每個線程負責往一定數量設備發送消息,主線程爲了收集n個線程的最終發送結果,NotificationThreads繼承了ThreadGroup,並將對象實例傳到每個子線程構造器中,對主線程wait,當所有的子線程執行完畢後,通過notifyAll喚醒所有進程,繼續後面的流程。

貌似沒有什麼明顯問題,但是mat的分析結果ThreadGroup裏面有大量其它線程,擔心會有干擾。決定採用一種更靠譜更安全的方式,通過CountDownLatch來控制。









b)在子任務結束後(無論是正常結束還是非正常結束),在finally裏面進行後續操作,關閉socket連接;

    另外 對 countDownLatch數量減1



重新打包並上傳maven倉庫(注意:需要修改pom配置文件)

<dependency>
  <groupId>com.github.fernandospr</groupId>
  <artifactId>javapns-jdk16</artifactId>
  <version>2.3.1-SNAPSHOT</version>
</dependency>

本地運行單元測試,可以成功推送消息


c)線上集羣部署了一臺機器,開始beta測試,運行一個1200W的推送任務


經過258次YGC後,年老代的空間使用率 依然很低,只有2%+

另外觀察S0、S1、E發現,一次YGC後 to交換區基本能滿足存放存活對象,不會有大量對象晉升到old區。


任務跑完後,gc情況,YGC 602次,沒有觸發Full GC



另外性能監控顯示已經發送了800多萬 條消息(注:圖中統計的是接口調用次數,每次接口調用傳100個用戶token),響應時間正常。


總結:

a)線上報警,無論load彪的有多高,又或cpu使用率100%,千萬不要慌,先保留一臺問題機器,其它的機器全部重啓,保證不影響外部使用

b)要從整個鏈路全面分析問題,多和身邊的同事溝通討論,也許會碰撞出靈感。


最後,非常感謝 錯刀、金磚 ,處理過程中,提供了很多幫助!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章