轉眼遊戲已經上線兩個月了,熬過了無數個通宵,經歷了很多磨難,不過最後總算是上線了吧。上線之後也自然會被玩家查出來很多bug,現在總結下算是給自己一個交代吧。

bug有jvm底層的bug,也有功能性的邏輯bug,也有設計缺陷,大部分是沒有考慮周全。1:熱更代碼之後伺服器crash原因:查看crash文件hs_err.log 發現異常幀

# V [libjvm.so+0x43c477] ciObjectFactory::create_new_metadata(Metadata*)+0x327

之後查看堆棧詳細信息Stack: [0x00007f5fa3216000,0x00007f5fa3317000], sp=0x00007f5fa3313670, free space=1013kNative frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)V [libjvm.so+0x43c477] ciObjectFactory::create_new_metadata(Metadata*)+0x327V [libjvm.so+0x43c8a5] ciObjectFactory::get_metadata(Metadata*)+0x85V [libjvm.so+0x434397] ciSpeculativeTrapData::translate_from(ProfileData const*)+0x67V [libjvm.so+0x4346a8] ciMethodData::load_extra_data()+0xa8V [libjvm.so+0x436828] ciMethodData::load_data()+0x268V [libjvm.so+0x4295a4] ciMethod::method_data()+0xb4

V [libjvm.so+0x959175] Parse::Parse(JVMState*, ciMethod*, float)+0x645

V [libjvm.so+0x3f6ed8] ParseGenerator::generate(JVMState*)+0x88V [libjvm.so+0x5709eb] Parse::do_call()+0x23bV [libjvm.so+0x9628e2] Parse::do_one_bytecode()+0x3382V [libjvm.so+0x953688] Parse::do_one_block()+0x178V [libjvm.so+0x9579b7] Parse::do_all_blocks()+0x127V [libjvm.so+0x959379] Parse::Parse(JVMState*, ciMethod*, float)+0x849V [libjvm.so+0x3f6ed8] ParseGenerator::generate(JVMState*)+0x88V [libjvm.so+0x4a959a] Compile::Compile(ciEnv*, C2Compiler*, ciMethod*, int, bool, bool, bool)+0x127aV [libjvm.so+0x3f5895] C2Compiler::compile_method(ciEnv*, ciMethod*, int)+0x1f5

V [libjvm.so+0x4b3d4a] CompileBroker::invoke_compiler_on_method(CompileTask*)+0xcba

V [libjvm.so+0x4b4cf6] CompileBroker::compiler_thread_loop()+0x5d6V [libjvm.so+0xa7f243] JavaThread::thread_main_inner()+0x103V [libjvm.so+0xa7f38c] JavaThread::run()+0x11cV [libjvm.so+0x92e0f8] java_start(Thread*)+0x108C [libpthread.so.0+0x79d1] start_thread+0xd1Current CompileTask:C2:199531680 82052 com.mrd.dolphin.tencentLog.AbstractTlogUtil::logPlayerExpFlow (274 bytes)這裡提示是JVM的JIT功能C2編譯器在編譯方法logPlayerExpFlow的時候拋出異常導致crash

其他的七台機器都在不同的方法編譯的時候拋出異常

首先去java官方的bug平台上查詢發現JDK8是有這個crash的問題的,這個問題是在JDK9的時候徹底修復但是目前更改JDK不現實,只能想方法繞過JDK8的這個問題。之後進行了一下思考:1、遊戲進過多次的測試,JVM從來沒有出現crash,就算JDK8有這個bug應該幾率也會非常低所以目前只能充分相信JVM的強壯性。2、這個問題發生的時候都是經過到了代碼熱更新,之前在pr2環境上熱更都是OK的,IDC機房熱更新就會出現這個問題。通過思考發現應該是目前的IDC環境和PR2環境在熱更新的時候有不一樣的邏輯處理點存在,這個不同點導致了crash的產生。開始尋找不同點的步驟1、在12號熱更新代碼的時候做過一個危險的操作:在遊戲進程運行的時候我們對進程的所有jar文件進行了全覆蓋操作。這個操作從來沒有做過,pr2和之前的測試都沒有做過,所以這個操作被標記成一個危險操作。2、之前的jar是使用JDK7編譯成JDK7規範的jar,但是上線前打包工具的JDK升級到了JDK8,使用JDK8編譯一個JDK7規範的jar,這一點也和pr2測試的時候不一樣,這個點也被定義成危險點所以對打包工具進行回退,回退成JDK7版本之後18號在對線上IDC進行代碼熱更後,不到一個小時就有伺服器crash。所以全覆蓋jar和JDK7的編譯不是crash的絕對原因。

之後繼續尋找不同的邏輯點。發現之前代碼熱更是通過CC伺服器發消息給遊戲伺服器,遊戲伺服器內部捕獲自己的PID進行類重載入操作,因為IDC部署的時候不能使用CC伺服器這樣的網頁操作,所以對熱更的方式進行了修改,改成運行一個第三方進程,使用第三方進程捕獲遊戲PID,通知遊戲進程進行類的重轉換操作。這一點操作IDC和PR2不一樣。所以修改了類重轉換操作,還是修改成遊戲進程自己捕獲自己的PID通知自己進行類的重轉換。

之後對IDC的遊客服進行熱更,觀察一周沒有crash,之後對所有線上服進行熱更新操作,目前看這個問題暫時解決。代碼熱更新20小時候會導致jvm宕機,究其原因是上線之後代碼做了改動並沒有做壓力測試導致。jvm 8 的代理模式存在底層bug,存在c2編譯導致宕機的隱患。解決方案:將外部代理模式改為內部處理機制2:回收高價值物品可以無限刷綁銀的bug原因:回收價格的計算公式沒有考慮到int最大值,導致最終結果出現負數解決方案:計算公式中的所有參數改為long值3:發放補償獎勵時,有概率發放兩封

原因:離線事件生成dbData多線程並發處理,由於不具備原子性,導致生成了兩條數據,因此發了兩封郵件

解決方案:離線事件被投遞時,先生成dbData,volatite只能保證內存可見性,不能保證原子性4:玩家上線後發現幫派界面錯亂原因:玩家先申請加入一個幫派,然後下線了。下線期間幫主批准入幫了發了加入幫派的離線事件,後又被幫主踢出幫派,當玩家上線時先收到踢出幫派事件,再收到加入幫派事件,導致幫忙系統出錯。原因:離線事件載入後被放到了一個map中,沒有按照離線事件的順序進行派發,導致邏輯層出錯解決方案:觸發離線事件時按照入庫順序排序,確保順序5:伺服器宕機數據丟失,如裝備屬性數據丟失原因:裝備基礎數據和屬性數據是分開存儲的,是強關聯的這種關係,宕機時很容產生數據一致性問題。即基礎數據存了,屬性數據沒有保存。導致玩家查看裝備時,屬性詞條等數據丟失。解決方案:添加裝備屬性關聯數據檢查,檢查失敗時自動修復(生成一組新的裝備屬性數據),這種解決方法有缺陷,如果玩家數據敏感,丟失重要裝備數據,玩家是要投訴的,可能需要額外補償或者恢複數據。

6:這個BUG被打開了兩次,但兩次的問題原因不同,具體如下:

1.玩家在10月22日上線時,幫派界面數據亂掉了。2.10月25日停服更新後,起服再登錄發現幫派數據亂掉了。原因:第一次問題:該玩家於10月12日下線,在10月22日上線後收到兩個離線事件,一個是加入幫派,一個是退出幫派。從離線事件guid和其他信息可知,加入幫派事件是先於被踢出幫派事件產生的。但實際上先執行了被踢出幫派事件。導致玩家身上的幫派GUID和幫派線程上的guid不一致了。第二次問題:該區活躍玩家非常少,幫花沒什麼人玩,導致長時間沒有數據更新,rides里緩存的數據全過期了。然後後面又寫入了2個新的數據,導致起服後rides里的數據不全。幫花數據丟失進一步導致同步幫派數據時發生了異常,數據沒有正常同步解決方案:第一次問題:雖然說這個BUG不是幫派系統本身引起的,但也反映的入幫退幫的一些流程問題,目前流程上太依賴離線事件比較脆弱。臨時修復方案:玩家登陸後,如果發現幫派guid和幫派線程上的不一致了,就校正一次。第二次問題:將幫派相關的寫入不頻繁的數據全部改為從DB直接讀取

先更新一部分,後續繼續跟進TODO

推薦閱讀:

相关文章