想少踩坑?不可忽視的JAVA註釋

諸多緣由,咱們的程序每每不能解釋其自己,再者,咱們也不能苛求調用者讀(懂)咱們的程序邏輯。因此,咱們須要給代碼添加註釋。好的代碼註釋規範是不可或缺的,尤爲是要給類和方法添加註釋。java

 

今天下午生產環境的一個服務出現java.lang.OutOfMemoryError: GC overhead limit exceeded,組內各夥伴齊參與,來查找緣由。redis

其中一個線索是,以下sql頻繁執行,致使日誌量激增超過1G。sql

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);
    }
}

 

想到的方案天然是用緩存。app

恰好,項目裏有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),感受沒問題就用上了。。。ide

 

以下是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;
    }

 

 

單看這樣的方法註釋,很難說將來別人使用的時候不會出現我踩的這個坑呀!測試

因而乎,我完善了一下方法的註釋,見下方,我以爲,若是往後還有人踩到這個坑的話,我只能呵呵嘿嘿哈哈了。spa

    /**
     * 緩存是否存在
     * 注意與{@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掉進程後,那些工做線程也都跟着死掉了。重啓後,沒有業務操做觸發這個方法的執行,因此,日誌量再也不激增了。

相關文章
相關標籤/搜索