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

logo

一個簡單的冪等工具類

在平常的工做中,業務的去重冪等場景屬於比較常見的需求,通常來說簡單的冪等工具類能夠基於內存或者基於redis進行,本篇簡單介紹下,如何使用Guava的緩存來實現一個冪等工具類java

I. 基本思路與實現

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

1. 基本實現

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

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爲上次計算的結果,所以在下次再次執行時,直接拿這個結果便可,適用於須要獲取計算結果做爲他用的業務場景中。那麼在實際使用中,直接這麼用是否可行?redis

答案倒是不行,在實際使用的時候,有幾個地方須要注意緩存

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

2. null值問題

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

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

在上面使用中,有一點須要注意,在取出數據以後,首先判斷下是否爲未命中狀態?爲何未命中要這麼幹?而言看博文工具

3. 內存問題

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

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

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

一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛3d

2. 聲明

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

3. 掃描關注

QrCode

相關文章
相關標籤/搜索