java.lang.IllegalArgumentException: argument type mismatch
異常背景
- 異常容災
- 實現方案
- Spring AOP 捕獲接口拋出的異常,保留當前類、接口、方法、入參信息
- 通過定時任務遍歷當前需要重試的任務列表
- 通過反射機制重新調用接口方法
- 實現方案
- 隊列消息
- 上游應用先將數據存入DB,再發送MQ通知下游應用
- 但往往MQ的接收速度快於DB存入速度,導致下游發起查詢功能時數據未錄入到DB中
- 解決方案
- 時間延遲,下游應用接收到隊列消息延遲5s再進行處理
- 查詢重試,5s 內不斷請求查詢接口直到查詢到數據
- 容災重試,記錄異常方法及入參,定時任務掃描重試,失敗達到指定次數後郵件預警通知,手動處理
- 異常描述
- 消息內容:由於是通過消息隊列傳遞信息,消息體爲JSONObject(org.json.JSONObject)
- 存儲接口:觸發容災後將接口入參轉爲JSON.toJSONString(底層實現爲ObjectMapper) 字符串存入DB
- 反射調用:在重組參數及接口方法反射過程中程序運行正常;在Reflection.invokeMethod時出現參數不匹配異常
- 檢查入參:此時檢查調用方法是傳入的params發現原本的org.json.JSONObject 被轉爲 map (LinkedHashMap) 形式
- 問題糾結
- 遇到問題時雖然檢查了參數的內容,想到了與預期的JSONObject不符,但是當時在想可能就是默認的對應的映射關係
- 查詢問題的重點放在了JSON工具的使用上,序列化與反序列化的工具使用,嘗試換了ali的fastjson等其他工具類後發現問題依舊
異常排查
- 單元測試
- 寫單元測試類只測試Reflection.invokeMethod方法,將org.json.JSONObject 轉爲 String ,再有 String 轉回 JSONObjct 調用 Reflection.invokeMethod 方法
- 此時發生轉換異常,原來的程序調用位置有多層try catch,未發現明顯異常
Exception in thread “main” java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to org.json.JSONObject
- 問題定位
- org.json.JSONObject 在 toString()時轉爲 Map
- Map 無法逆向直接轉爲 org.json.JSONObject
異常解決
- 在調用Reflection.invokeMethod方法前獲取參數時
- 判斷如果是org.json.JSONObject.class 手動將參數Map轉爲JSONObject
- 問題解決
異常反思
-
認爲最不可能出現問題的地方往往就是問題所在
- 發現生產環境服務訪問偶爾出現丟包
- 檢查各個機器節點響應,檢查網絡連接,查詢日誌
- 最終問題定位原因在於硬件故障
- 雖然一開始討論時有人懷疑過硬件問題,但是因爲這個原因發生的可能性太低了,所以一開始就被排除了
- 需要通過逐個機器停機測試的方式排查來最終定位
- 上述異常排查時只檢查了Reflection.invoke方法參數入參,發現參數無誤,沒有仔細對照參數類型,或直接以爲JSONObject與Map之間就是互逆轉換,直接把排查問題的方向定位到JSON序列化的工具上,方向偏了,越走越遠
- 發現生產環境服務訪問偶爾出現丟包
-
適可而止,不要急於求成
- 不要一條道跑到黑,三十分鐘沒有結論,就說明要不方向錯了,要不解決方案不正確,及時調整方向,避免過度糾結