使用LRUMap实现
import org.apache.commons.collections4.map.LRUMap;
/**
* 幂等性判断
*/
public class IdempotentUtils {
// 根据 LRU(Least Recently Used,最近最少使用)算法淘汰数据的 Map 集合,最大容量 100 个
private static LRUMap reqCache = new LRUMap<>(100);/**
* 幂等性判断
* @return
*/public static boolean judge(String id, Object lockClass) {
synchronized (lockClass) {
// 重复请求判断
if (reqCache.containsKey(id)) {
// 重复请求
System.out.println("请勿重复提交!!!" + id);return false;
}// 非重复请求,存储请求 ID
reqCache.put(id, 1);
}return true;
}
}
LRUMap+Caffeine
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
import com.github.benmanes.caffeine.cache.*;
import org.apache.commons.collections4.map.LRUMap;
import org.apache.commons.lang3.StringUtils;
import java.util.concurrent.*;
/**
* @author beike
*/
public class IdempotentUtils {
/**
* 根据 LRU(Least Recently Used,最近最少使用)算法淘汰数据的 Map 集合,最大容量 100 个
*/
private static final LRUMap<String, Integer> REQ_CACHE = new LRUMap<>(100);
/**
* 同步加载
* 弱引用不管当前内存空间足够与否,都会回收它的内存
*/
private static final LoadingCache<String, String> SYNC_LOADING_CACHE = Caffeine.newBuilder()
//数量上限
.maximumSize(1_000)
//初始容量
.initialCapacity(128)
// 弱引用value
.weakValues()
// 过期机制
.expireAfterWrite(5, TimeUnit.SECONDS)
//刷新机制
.refreshAfterWrite(5, TimeUnit.SECONDS)
.removalListener((RemovalListener<String, String>) (key, value, cause) ->
System.out.println("key:" + key + ", value:" + value + ", 删除原因:" + cause.toString()))
.build(key -> null);
/**
* Caffeine 同步加载
*
* @param key 一般是请求的url+业务id
* @return 重复提交 true 第一次提交
*/
public static boolean judgeCaffeineSync(String key) {
String value = SYNC_LOADING_CACHE.get(key);
// 重复请求判断
if (StringUtils.isNotBlank(value)) {
return false;
}
// 非重复请求,存储请求
SYNC_LOADING_CACHE.put(key, key);
return true;
}
/**
* 双重检测锁 DCL+LRUMap
*
* @param key 一般是请求的url+业务id
* @param lockClass 服务层Class
* @return 重复提交 true 第一次提交
*/
public static boolean judgeLruMap(String key, Object lockClass) {
if (REQ_CACHE.containsKey(key)) {
// 重复请求
return false;
}
synchronized (lockClass) {
// 重复请求判断
if (REQ_CACHE.containsKey(key)) {
return false;
}
// 非重复请求,存储请求key
REQ_CACHE.put(key, 1);
}
return true;
}
}
@PutMapping("/update")
@ApiOperation(value = "更新商品", httpMethod = "PUT")
public ResponseData<String> update(@RequestBody @Validated Vo vo) {
log.info("{}用户更新,{}", TokenUtil.getCurrentUserName(), JSON.toJSONString(vo));
if (!IdempotentUtils.judgeCaffeineSync("/v1/goods/update/" + vo.getId())) {
return ResponseUtil.fail(MessageConstant.SYS_REPEAT_SUMMIT_FAIL);
}
//todo 执行业务代码
service.update(vo);
return ResponseUtil.success();
}