1. 程式人生 > >180705-一個簡單的冪等工具類實現

180705-一個簡單的冪等工具類實現

scheduled check final https 內存問題 fault 可能 使用 關系

技術分享圖片

一個簡單的冪等工具類

在日常的工作中,業務的去重冪等場景屬於比較常見的需求,一般來講簡單的冪等工具類可以基於內存或者基於redis進行,本篇簡單介紹下,如何使用Guava的緩存來實現一個冪等工具類

I. 基本思路與實現

利用Guava的內存緩存來緩存,如果執行完畢,則在緩存中添加一個標識,每次執行之前,判斷是否執行過,從而實現簡單的冪等邏輯

1. 基本實現

基於此,一個簡單的工具來就出爐了

public static final String NOT_HIT_TAG = "UNHIT_TAG";

private static LoadingCache<String, Object> idempotentCache =
    CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
        @Override
        public Object load(String key) throws Exception {
            return NOT_HIT_TAG;
        }
});

public static Object getObject(String uuid) {
    return idempotentCache.getUnchecked(uuid);
}

上面的代碼比較簡單,這個冪等工具類,key為唯一標識,value為上次計算的結果,因此在下次再次執行時,直接拿這個結果即可,適用於需要獲取計算結果作為他用的業務場景中。那麽在實際使用中,直接這麽用是否可行?

答案卻是不行,在實際使用的時候,有幾個地方需要註意

  • 如果某次計算結果返回的null怎麽辦?
  • 內存是否會爆掉?

2. null值問題

針對返回結果為null的場景,也好解決,就是利用一個符號來代替null,簡單的變形如下

public static final String NOT_HIT_TAG = "UNHIT_TAG";
public static final String NULL_TAG = "NULL_TAG";
private static LoadingCache<String, Object> idempotentCache =
        CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
            @Override
            public Object load(String key) throws Exception {
                return NOT_HIT_TAG;
            }
        });

public static Object getObject(String uuid) {
    Object obj = idempotentCache.getUnchecked(uuid);
    if (obj instanceof String) {
        if (NULL_TAG.equals(obj)) {
            return null;
        }
    }

    return obj;
}

public static void putObject(String uuid, Object val) {
    if (val == null) {
        val = NULL_TAG;
    }

    idempotentCache.put(uuid, val);
}

在上面使用中,有一點需要註意,在取出數據之後,首先判斷下是否為未命中狀態?為什麽未命中要這麽幹?而言看博文

  • 180613-GuavaCache返回Null的註意事項

3. 內存問題

雖然上面設置了失效時間為3min,但在jdk8的場景下,很容易發現內存瘋狂上漲,不見到有回收? why?這塊可能與gauva的內存回收機制有關系,因為jdk8取消了永久代,使用了元空間,當沒有設最大值時,會一直上漲,使用系統的內存

簡單的解決方案就是主動回收掉無效的數據

public static final String NOT_HIT_TAG = "UNHIT_TAG";
public static final String NULL_TAG = "NULL_TAG";
private static LoadingCache<String, Object> idempotentCache =
        CacheBuilder.newBuilder().expireAfterAccess(3, TimeUnit.MINUTES).build(new CacheLoader<String, Object>() {
            @Override
            public Object load(String key) throws Exception {
                return NOT_HIT_TAG;
            }
        });

public static Object getObject(String uuid) {
    Object obj = idempotentCache.getUnchecked(uuid);
    if (obj instanceof String) {
        if (NULL_TAG.equals(obj)) {
            return null;
        }
    }

    return obj;
}

public static void putObject(String uuid, Object val) {
    if (val == null) {
        val = NULL_TAG;
    }

    idempotentCache.put(uuid, val);
}

public static void remove(String uuid) {
    idempotentCache.invalidate(uuid);
}

public static void registerScheduleClearTask() {
    ScheduledExecutorService task =
            Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("idempotent"));
    task.scheduleAtFixedRate(() -> idempotentCache.cleanUp(), 1, 1, TimeUnit.MINUTES);
}

II. 其他

1. 一灰灰Blog: https://liuyueyi.github.io/hexblog

一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛

2. 聲明

盡信書則不如,已上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激

  • 微博地址: 小灰灰Blog
  • QQ: 一灰灰/3302797840

3. 掃描關註

技術分享圖片

180705-一個簡單的冪等工具類實現