諸多原因,我們的程式往往不能解釋其本身,再者,我們也不能苛求呼叫者讀(懂)我們的程式邏輯,所以,我們需要給代碼添加注釋,好的代碼注釋規范是不可或缺的,尤其是要給類和方法添加注釋,
今天下午生產環境的一個服務出現java.lang.OutOfMemoryError: GC overhead limit exceeded,組內各伙伴齊參與,來查找原因,
其中一個線索是,如下sql頻繁執行,導致日志量激增超過1G,
2021-04-13 13:35:01.023 [DEBUG] [http-apr-8480-exec-9] [com.yft.mapper.TMerchantDAO.selectMerchantByMerId:132] ==> Preparing: select * from T_MERCHANT where MER_ID=? 2021-04-13 13:35:01.024 [DEBUG] [http-apr-8480-exec-9] [com.yft.mapper.TMerchantDAO.selectMerchantByMerId:132] ==> Parameters: 89900000617916182868(String) 2021-04-13 13:35:01.025 [TRACE] [http-apr-8480-exec-9] [com.yft.mapper.TMerchantDAO.selectMerchantByMerId:138] <== Columns: ID, MER_ID, MER_NAME, PROVINCE, CITY, COUNTY, REG_ADDR, LEGAL_PERSON, LEGAL_ID_CARD, AUTH_MOBILE, AUTH_EMAIL, SIGNING_PLAT_TYPE, DOMAIN_NAME, RECORD_NO, APP_NAME, APP_MARKET, MCC_1, MCC_2, BUSINESS_NAME, BUSINESS_MOBILE, BUSINESS_EMAIL, FINANCE_NAME, FINANCE_MOBILE, FINANCE_EMAIL, IT_NAME, IT_MOBILE, IT_EMAIL, IT_QQ, POST_NAME, POST_MOBILE, CONTRACT_POST_ADDR, AGENT_ID, SALE_ID, SETTLE_ACC_NAME, SETTLE_ACC_BANK, SETTLE_ACC_NO, SETTLE_CYCLE, SETTLE_TYPE, LEVY_ID, CUS_CONTRACT_TYPE, TAX_COMPANY, TAX_NO, TAX_ADDR, TAX_MOBILE, TAX_OPEN_BANK, TAX_ACC, STATE, AUDIT_STATE, PAY_CHANNEL, FILE_PATH, FIRST_AUDIT_TIME, REVIEW_AUDIT_TIME, MEMO, CREATE_TIME, UPDATE_TIME, OPERATOR, FIRST_AUDITOR, REVIEW_AUDITOR, SUBMIT_TIME, OPEN_TIME, CONTRACT_FILE_PATH, MINI_PIC_PATH, OTHER_NAME, MER_BEAR_TAX, INVOICE_TYPE, IS_SET_AUDIT, SIGNING_PARAM_VALID, CHECK_TYPE, CHECK_MOBILES, MERCHANT_TYPE, GROUP_MER_ID, AUTH_MSG, AGREEMENT_TYPE, IS_CONFIRM, IS_DELIVER, IS_CONFIRM_PROJECT, ENTERPRISE_ID
通過查程式,這個TMerchantDAO#selectMerchantByMerId方法有20多處呼叫,由于是外采的老系統,以及后續需求迭代缺乏有效管控,造成我們短時間內不能定位每個呼叫的具體用途,而服務的log檔案還在不斷增大,20分鐘就會增加0.1G,當然,可想而知db壓力也不小,權宜之計,有必要立刻馬上趕緊減少這個方法的執行次數,
@Service @Slf4j public class TMerchantServiceImpl implements TMerchantService { @Override public TMerchant selectMerchantByMerId(Map<String, Object> map) { return tMerchantDAO.selectMerchantByMerId(map); } }
想到的方案自然是用快取,
剛好,專案里有redis工具類JedisUtils,于是,三下五除二,修改程式為如下版本,并找了兩個小伙伴review,沒發現什么問題,立即找運維先合代碼重啟服務,
@Service @Slf4j public class TMerchantServiceImpl implements TMerchantService { @Override public TMerchant selectMerchantByMerId(Map<String, Object> map) { // return tMerchantDAO.selectMerchantByMerId(map); return selectMerchantByMerId_Cache(map); } public TMerchant selectMerchantByMerId_Cache(Map<String, Object> map) { String merId = String.valueOf(map.get("merId")); String key="TMerchant:".concat(merId); if(JedisUtils.exists(key)){ logger.info("read from redis. key={}", key); return (TMerchant) JedisUtils.getObject(key); } else { TMerchant tMerchant = tMerchantDAO.selectMerchantByMerId(map); JedisUtils.setObject(key, tMerchant, 30); return tMerchant; } } }
重啟之后,日志不再激增,不過,奇怪的是,log里并未發現read from redis. key=,因為有其他的事情處理,困擾了一下午的這個問題,到晚上下班后,經過本地撰寫testcase測驗才發現,上面呼叫JedisUtils的exists(String)是不對的,而應該呼叫JedisUtils的另一個方法existsObject(String),,,而我當時卻并未注意到還有existsObject(String)這個方法,只是快速掃了一下exists(String),感覺沒問題就用上了,,,
如下是JedisUtils里這兩個方法的定義
/** * 快取是否存在 * @param key 鍵 * @return */ public static boolean exists(String key) { boolean result = false; Jedis jedis = null; try { jedis = getResource(); result = jedis.exists(key); logger.debug("exists {}", key); } catch (Exception e) { logger.warn("exists {}", key, e); } finally { returnResource(jedis); } return result; } /** * 快取是否存在 * @param key 鍵 * @return */ public static boolean existsObject(String key) { boolean result = false; Jedis jedis = null; try { jedis = getResource(); result = jedis.exists(getBytesKey(key)); logger.debug("existsObject {}", key); } catch (Exception e) { logger.warn("existsObject {}", key, e); } finally { returnResource(jedis); } return result; }
單看這樣的方法注釋,很難說未來別人使用的時候不會出現我踩的這個坑呀!
于是乎,我完善了一下方法的注釋,見下方,我覺得,如果日后還有人踩到這個坑的話,我只能呵呵嘿嘿哈哈了,
/** * 快取是否存在 * 注意與{@link #existsObject(String)}的區別,本方法適用于set設定值,而后者適用于通過setObject來設定值 * @param key 鍵 * @return */ public static boolean exists(String key) { boolean result = false; Jedis jedis = null; try { jedis = getResource(); result = jedis.exists(key); logger.debug("exists {}", key); } catch (Exception e) { logger.warn("exists {}", key, e); } finally { returnResource(jedis); } return result; } /** * 快取是否存在 * 注意與{@link #exists(String)}的區別,本方法適用于setObject設定值,而后者適用于通過set來設定值 * @param key 鍵 * @return */ public static boolean existsObject(String key) { boolean result = false; Jedis jedis = null; try { jedis = getResource(); result = jedis.exists(getBytesKey(key)); logger.debug("existsObject {}", key); } catch (Exception e) { logger.warn("existsObject {}", key, e); } finally { returnResource(jedis); } return result; }

后話,為什么服務重啟完之后,log不再激增了,通過查看日志,那個select * from T_MERCHANT where MER_ID=?的方法不再執行了,顯然,kill掉行程后,那些作業執行緒也都跟著死掉了,重啟后,沒有業務操作觸發這個方法的執行,所以,日志量不再激增了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/275659.html
標籤:Java
