基于ConcurrentHashMap实现带时限次数限制的缓存
需求
需要实现一个对准确性要求不高的 每个客户 每小时请求次数 不超过100次的限制,用于防止接口被刷。
思考
- 既然对准确性要求不高,那么实现一个简易高效的 web 缓存足以。
- 缓存信息包括 客户、时间、次数,用 map 来存比较合适。
大概思路有了,正打算面向浏览器编程时,脑海里秃然非常清晰想到怎么做了,于是开开心心地写了起来。
实现
1)先有一个实体类 LimitEntity
public class LimitEntity {
Long beginTime = 0L;
int times = 0;
public Long getBeginTime() {
return beginTime;
}
public void setBeginTime(Long beginTime) {
this.beginTime = beginTime;
}
public int getTimes() {
return times;
}
public void setTimes(int times) {
this.times = times;
}
}
2)再有一个缓存实现类
import java.util.Calendar;
import java.util.concurrent.ConcurrentHashMap;
public class TestConcurrentHashMap {
private static long timeLimit = 3600*1000; //时限 1小时
private static int timesLimit = 10; //次数限制 10次
private static ConcurrentHashMap<Integer, LimitEntity> cacheMap = new ConcurrentHashMap<Integer, LimitEntity>();
public static void main(String[] args) {
int aid = 1;
for (int i=0; i<=timesLimit; i++){
count(aid, Calendar.getInstance().getTimeInMillis());
}
}
public static void count(int aid, long now){
LimitEntity limitEntity = cacheMap.get(aid);
int times = 1;
if (limitEntity == null){
//首次访问的用户,put 进缓存
limitEntity = new LimitEntity();
limitEntity.setBeginTime(now);
limitEntity.setTimes(times);
cacheMap.put(aid, limitEntity);
}else {
long beginTime = limitEntity.getBeginTime();
if (now - beginTime > timeLimit){
//超出时限1小时,重置开始时间和次数
limitEntity.setBeginTime(now);
limitEntity.setTimes(times);
cacheMap.put(aid, limitEntity);
}else {
times = limitEntity.getTimes()+1;
if (times > timesLimit){
//超出次数限制,提示
System.out.println("超出限制");
return;
}else {
//没超限制,更新次数
limitEntity.setTimes(times);
cacheMap.put(aid, limitEntity);
}
}
}
System.out.println("aid=" + aid + ";now - beginTime=" + (now - limitEntity.getBeginTime()) + ";times=" + limitEntity.getTimes());
}
}
3)主要步骤
如代码,看注释应该很好理解了。
- 首次访问的用户 -> put 进缓存
- 超出时限1小时 -> 重置开始时间和次数
- 超出次数限制 -> 提示
- 没超限制 -> 更新次数
运行效果
以上。如果有更优雅的办法欢迎留言讨论喔。