开始
本篇开始分析package cache。
cacheObj
cacheObj用来记录访问数据,有2个属性,其中value用来存储单位时间(某个时间窗口,可以自定义时长)内ip访问次数,expire表示从第一次请求开始到时间窗口结束的时间戳。
例如,第一次访问,value=1,expire=当前时间戳+时间窗口长度,也就是期满时间。
存储的数据结构为:
private static final Map<String, CacheObj> STORE = new ConcurrentHashMap<>()
用这个数据结构可以表示某个ip在一定时间窗口内的访问次数。其中map的key用ip等关键值来生成,cacheObj存储访问数据。
cache接口
其中以下几个方法属于缓存比较基通用的方法。写入某个值,获取某个值,判断某个值是否村存在,删除某个值,获取某个值失效时间,清除缓存。
void set(String key, Integer value);
CacheObj get(String key);
Boolean hasKey(String key);
void del(String key);
long getExpire(String key);
default void clear();
还有两个方法 ,属于带有业务属性,下面看具体的实现。
void set(String key, Integer value, long delay, TimeUnit unit);
int incrementAndGet(String key);
cache接口的两种实现
ConcurrentHashMapCache
- 定期清理缓存
通过CacheScheduler每5分钟清理过期无效的缓存,清除的标准是cacheObj.expire小于当前的时间戳System.currentTimeMillis()。 - 方法和锁
方法 | 锁 |
---|---|
set | 写锁 |
get | 读锁 |
hasKey | 读锁 |
del | 写锁 |
getExpire | 读锁 |
incrementAndGet | 无锁 |
- set方法写锁保护,防止多线程操作的时候出错。
@Override
public void set(String key, Integer value) {
writeLock.lock();
long delay = properties.getInterval();
TimeUnit unit = TimeUnit.MILLISECONDS;
try {
CacheObj cacheObj = STORE.get(key);
if (null == cacheObj) {
this.set(key, value, delay, unit);
} else {
cacheObj.setValue(value);
STORE.put(key, cacheObj);
}
} finally {
writeLock.unlock();
}
}
- incrementAndGet的实现,这里每次访问请求把计数增加1,这里并没有显示调用锁,但是可以看到,这边get方法和set方法使用了读锁。
@Override
public int incrementAndGet(String key) {
int value = 0;
CacheObj cacheObj = get(key);
if (null == cacheObj) {
value = 1;
} else {
value = cacheObj.getValue() + 1;
}
this.set(key, value);
return value;
}
Redis的实现
由于redis是单线程操作,没有多线程并发的问题,所以相对比较简单。基本的方法都是调用BraumRedisAutoConfiguration里面的RedisTemplate来实现。
最后
更多源码解析关注我学源码网