缓存模式
public class Cache {
/**
* Caching Pattern【缓存模式】
* readThrough:先尝试从缓存中读取数据,如果存在,则直接返回缓存中的数据;如果不存在,则从数据源加载,
* 数据源中存在目标对象,则将其写入缓存,并返回加载的对象。
* writeThrough:先将目标对象写入数据源,再将其写入缓存中
* writeAround:先将目标对象写入数据源,则将缓存失效
* writeBehind:如果缓存已满 && 目标对象不再缓存中,则将 LRU 对象从缓存中移除,并异步将其写入数据源中,
* 同时将目标对象写入缓存中。
* cacheAside:readThrough && writeAround
*/
@Test
public void all() {
final CacheStore store = new CacheStore();
final long id = 1L;
store.writeThrough(User.of(id, "zxd"));
final User user = store.readThrough(id);
store.writeAround(user);
store.readThrough(id);
store.writeBehind(User.of(2L, "princess"));
}
}
@Value(staticConstructor = "of")
class User {
private Long id;
private String name;
}
class LruCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = 3409436250531011182L;
private final int maxSize;
public LruCache(int maxSize) {
super(16, 0.75F, true);
this.maxSize = maxSize;
}
public Entry<K, V> removeEldest() {
final Entry<K, V> entry = entrySet().iterator().next();
if (Optional.ofNullable(entry).isPresent()) {
remove(entry.getKey());
}
return entry;
}
public boolean isfull() {
return maxSize >= size();
}
}
class DataSource {
private static ConcurrentMap<Long, User> users = new ConcurrentHashMap<>();
public static User load(Long id) {
return users.get(id);
}
public static void persist(User user) {
users.put(user.getId(), user);
}
}
@Slf4j
class CacheStore {
LruCache<Long, User> cache = new LruCache<>(1);
public User readThrough(Long id) {
User user;
// 1)首先尝试从缓存中读取,如果存在则直接返回
if (Optional.ofNullable(user = cache.get(id)).isPresent()) {
log.info("get from cache {}", user.getId());
return user;
}
// 2)如果缓存中不存在,则从数据源读取,如果读取到值,则将其写入缓存中
final User load = DataSource.load(id);
Optional.ofNullable(load).ifPresent(u -> {
log.info("load data from dataSource and set cache {}", u.getId());
cache.put(u.getId(), u);
});
// 返回读取的结果
return load;
}
public void writeThrough(User user) {
// 1)将目标对象持久化到数据源
DataSource.persist(user);
// 2)将其更新到缓存中
cache.put(user.getId(), user);
log.info("update dataSource and set cache{}", user.getId());
}
public void writeAround(User user) {
// 1)将目标对象持久化到数据源
DataSource.persist(user);
// 2)使得缓存失效【即将其从缓存中移除】
cache.remove(user.getId());
log.info("update dataSource and invalid cache {}", user.getId());
}
public void writeBehind(User user) {
// 1)如果缓存已满 && 目标值未在缓存中 && LRU移除缓存并将其持久化到数据源中
if (cache.isfull() && !cache.containsKey(user.getId())) {
final Entry<Long, User> entry = cache.removeEldest();
final User value = entry.getValue();
log.info("async update dataSource {}", value.getId());
// 异步将数据写入数据源
CompletableFuture.runAsync(() -> {
DataSource.persist(value);
});
}
// 2)将目标对象更新到缓存中
cache.put(user.getId(), user);
}
public User readAside(Long id) {
return readThrough(id);
}
public void writeAside(User user) {
writeAround(user);
}
}