使用LRUMap实现

  1. import org.apache.commons.collections4.map.LRUMap;
  2. /**
  3. * 幂等性判断
  4. */
  5. public class IdempotentUtils {
  6. // 根据 LRU(Least Recently Used,最近最少使用)算法淘汰数据的 Map 集合,最大容量 100 个
  7. private static LRUMap reqCache = new LRUMap<>(100);/**
  8. * 幂等性判断
  9. * @return
  10. */public static boolean judge(String id, Object lockClass) {
  11. synchronized (lockClass) {
  12. // 重复请求判断
  13. if (reqCache.containsKey(id)) {
  14. // 重复请求
  15. System.out.println("请勿重复提交!!!" + id);return false;
  16. }// 非重复请求,存储请求 ID
  17. reqCache.put(id, 1);
  18. }return true;
  19. }
  20. }

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