在平常的工做中,业务的去重幂等场景属于比较常见的需求,通常来说简单的幂等工具类能够基于内存或者基于redis进行,本篇简单介绍下,如何使用Guava的缓存来实现一个幂等工具类java
<!-- more -->git
利用Guava的内存缓存来缓存,若是执行完毕,则在缓存中添加一个标识,每次执行以前,判断是否执行过,从而实现简单的幂等逻辑github
基于此,一个简单的工具来就出炉了redis
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为上次计算的结果,所以在下次再次执行时,直接拿这个结果便可,适用于须要获取计算结果做为他用的业务场景中。那么在实际使用中,直接这么用是否可行?缓存
答案倒是不行,在实际使用的时候,有几个地方须要注意ide
针对返回结果为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); }
在上面使用中,有一点须要注意,在取出数据以后,首先判断下是否为未命中状态?为何未命中要这么干?而言看博文学习
虽然上面设置了失效时间为3min,但在jdk8的场景下,很容易发现内存疯狂上涨,不见到有回收? why?这块可能与gauva的内存回收机制有关系,由于jdk8取消了永久代,使用了元空间,当没有设最大值时,会一直上涨,使用系统的内存ui
简单的解决方案就是主动回收掉无效的数据spa
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); }
一灰灰的我的博客,记录全部学习和工做中的博文,欢迎你们前去逛逛
尽信书则不如,已上内容,纯属一家之言,因我的能力有限,不免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激