在平常的工做中,業務的去重冪等場景屬於比較常見的需求,通常來說簡單的冪等工具類能夠基於內存或者基於redis進行,本篇簡單介紹下,如何使用Guava的緩存來實現一個冪等工具類java
利用Guava的內存緩存來緩存,若是執行完畢,則在緩存中添加一個標識,每次執行以前,判斷是否執行過,從而實現簡單的冪等邏輯git
基於此,一個簡單的工具來就出爐了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的場景,也好解決,就是利用一個符號來代替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); }
在上面使用中,有一點須要注意,在取出數據以後,首先判斷下是否爲未命中狀態?爲何未命中要這麼幹?而言看博文工具
雖然上面設置了失效時間爲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); }
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛3d
盡信書則不如,已上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激