代码:
package com.boot.base.common.util.cache;
import org.apache.commons.collections.map.LRUMap;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
/**
* @Description 处理请求幂等性
* @Author admin
* @Date 2021/3/3
*/
public class IdempotentUtils {
/**
* 默认的防抖动时间:3分钟(假设最佳情况为:每次所有请求对应的业务处理数据小于3分钟,不用默认则调用时单独指定!)
* 注意:
* 防抖动时间不能太长(因为有些请求正常流程中可以重复,如:补卡审批未通的情况下),
* 也不能太短(如:最后的结算结果入库前的业务逻辑比较长时!)
*/
public static final int DEFAULT_ANTI_JITTER_TIME = 3 * 60 * 1000;
/**
* 默认的缓存过期时间:10分钟
*/
public static final int DEFAULT_EXPIRE_TIME = 10 * 60 * 1000;
/**
* 定期清理缓存过期数据的周期:60分钟
*/
private static final int CLEAN_UP_PERIOD = 60 * 60 * 1000;
/**
* 缓存
*/
private static final Map<Object, CacheObject> cache = Collections.synchronizedMap(new LRUMap(512));
// 类加载时启动定期清理缓存的守护线程
static {
Thread cleanerThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(CLEAN_UP_PERIOD);
// 清理过期的缓存
cache.entrySet().removeIf(e -> Optional.ofNullable(e.getValue()).map(CacheObject::isExpired).orElse(false));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
cleanerThread.setName("IdempotentUtils-cleanerThread");
cleanerThread.setDaemon(true);
cleanerThread.start();
}
public static void main(String[] args) throws InterruptedException {
// for (int i = 0; i < 3; i++) {
// IdempotentUtils.check(i + "i", IdempotentUtils.class, System.currentTimeMillis());
// }
// System.out.println("从缓存中取出值:");
// IdempotentUtils.cache.entrySet().forEach(System.out::println);
// Thread.sleep(3000L);
// System.out.println("3秒钟过后");
// System.out.println("从缓存中取出值:");
// IdempotentUtils.cache.entrySet().forEach(System.out::println);
// Thread t = new Thread(() -> {
// while (!Thread.currentThread().isInterrupted()) {
// try {
// Thread.sleep(CLEAN_UP_PERIOD);
// // 清理过期的缓存
// System.out.println(Thread.currentThread().getName() + " sleep " + CLEAN_UP_PERIOD + " le");
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// }
// }
// });
//
// t.setName("t");
// t.setDaemon(true);
// t.start();
// Thread.sleep(7000L);
// System.out.println("main ready to exit");
// Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// System.out.println("jvm exit");
// }));
}
/**
* 校验是否为连续地重复请求,若相邻的2次请求时间间隔小于默认防抖时间,则视为连续的重复请求
*
* @param req 请求数据
* @param lock
* @return true-重复, false-不重复
*/
public static boolean check(Object req, Object lock) {
return check(req, lock, DEFAULT_ANTI_JITTER_TIME, DEFAULT_EXPIRE_TIME);
}
/**
* 校验是否为连续地重复请求,若相邻的2次请求时间间隔小于防抖时间antiJitterTime,则视为连续的重复请求
*
* @param req 请求数据
* @param lock
* @param antiJitterTime 防抖动时间,单位:毫秒
* @return true-重复, false-不重复
*/
public static boolean check(Object req, Object lock, int antiJitterTime) {
return check(req, lock, antiJitterTime, DEFAULT_EXPIRE_TIME);
}
/**
* 校验是否为连续地重复请求,若相邻的2次请求时间间隔小于防抖时间antiJitterTime,则视为连续的重复请求
*
* @param req 请求数据
* @param lock
* @param antiJitterTime 防抖动时间,单位:毫秒
* @param expireTimeMillis 过期时间,单位:毫秒
* @return true-重复, false-不重复
*/
public static boolean check(Object req, Object lock, int antiJitterTime, int expireTimeMillis) {
final long reqTimeMillis = System.currentTimeMillis();
synchronized (lock) {
if (cache.containsKey(req) && (reqTimeMillis - (long) cache.get(req).value <= antiJitterTime)) {
final long expireTime = reqTimeMillis + expireTimeMillis;
cache.put(req, new CacheObject(reqTimeMillis, expireTime));
return true;
}
final long expireTime = reqTimeMillis + expireTimeMillis;
cache.put(req, new CacheObject(reqTimeMillis, expireTime));
}
return false;
}
private static class CacheObject {
private Object value;
private long expireTime;
public CacheObject(Object value, long expireTime) {
this.value = value;
this.expireTime = expireTime;
}
public boolean isExpired() {
return System.currentTimeMillis() > this.expireTime;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
}
参考:
参考3:Java 内存缓存工具类
参考4:谈谈什么是守护线程以及作用 ?
参考5:分布式场景中的幂等redis实现