防重放机制的实现方法----LRUMap轻松解决

Apache 为我们提供了一个 commons-collections 的框架,里面有一个非常好用的数据结构 LRUMap 可以保存指定数量的固定的数据,并且它会按照 LRU 算法,帮你清除最不常用的数据。
LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的数据淘汰算法,选择最近最久未使用的数据予以淘汰。

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

实现如下:

import com.example.idempote.util.IdempotentUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/user")
@RestController
public class UserController4 {
    @RequestMapping("/add")
    public String addUser(String id) {
        // 非空判断(忽略)...
        // -------------- 幂等性调用(开始) --------------
        if (!IdempotentUtils.judge(id, this.getClass())) {
            return "执行失败";
        }
        // -------------- 幂等性调用(结束) --------------
        // 业务代码...
        System.out.println("添加用户ID:" + id);
        return "执行成功!";
    }
}
//LRUMap 的本质是持有头结点的环回双链表结构,它的存储结构如下:
AbstractLinkedMap.LinkEntry entry;
//当调用查询方法时,会将使用的元素放在双链表 header 的前一个位置,源码如下:
public V get(Object key, boolean updateToMRU) {
    LinkEntry entry = this.getEntry(key);if (entry == null) {return null;
    } else {if (updateToMRU) {this.moveToMRU(entry);
        }return entry.getValue();
    }
}protected void moveToMRU(LinkEntry entry) {if (entry.after != this.header) {
        ++this.modCount;if (entry.before == null) {throw new IllegalStateException("Entry.before is null. This should not occur if your keys are immutable, and you have used synchronization properly.");
        }
        entry.before.after = entry.after;
        entry.after.before = entry.before;
        entry.after = this.header;
        entry.before = this.header.before;this.header.before.after = entry;this.header.before = entry;
    } else if (entry == this.header) {throw new IllegalStateException("Can't move header to MRU This should not occur if your keys are immutable, and you have used synchronization properly.");
    }
}

//如果新增元素时,容量满了就会移除 header 的后一个元素,添加源码如下:
 protected void addMapping(int hashIndex, int hashCode, K key, V value) {
     // 判断容器是否已满 
     if (this.isFull()) {
         LinkEntry reuse = this.header.after;boolean removeLRUEntry = false;if (!this.scanUntilRemovable) {
             removeLRUEntry = this.removeLRU(reuse);
         } else {while(reuse != this.header && reuse != null) {if (this.removeLRU(reuse)) {
                     removeLRUEntry = true;break;
                 }
                 reuse = reuse.after;
             }if (reuse == null) {throw new IllegalStateException("Entry.after=null, header.after=" + this.header.after + " header.before=" + this.header.before + " key=" + key + " value=" + value + " size=" + this.size + " maxSize=" + this.maxSize + " This should not occur if your keys are immutable, and you have used synchronization properly.");
             }
         }if (removeLRUEntry) {if (reuse == null) {throw new IllegalStateException("reuse=null, header.after=" + this.header.after + " header.before=" + this.header.before + " key=" + key + " value=" + value + " size=" + this.size + " maxSize=" + this.maxSize + " This should not occur if your keys are immutable, and you have used synchronization properly.");
             }this.reuseMapping(reuse, hashIndex, hashCode, key, value);
         } else {super.addMapping(hashIndex, hashCode, key, value);
         }
     } else {super.addMapping(hashIndex, hashCode, key, value);
     }
 }

// LRUMap 判断容量的源码
public boolean isFull() {
  return size >= maxSize;
}
//容量未满就直接添加数据:
super.addMapping(hashIndex, hashCode, key, value);

综合来说:LRUMap 的本质是持有头结点的环回双链表结构,当使用元素时,就将该元素放在双链表 header 的前一个位置,在新增元素时,如果容量满了就会移除 header 的后一个元素。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值