一、概述
前言:
需求背景: 获取实时电量(调用
GRPC)问题:电量每5分钟更新,查询频繁,接口访问经常超时。
缓存,貌似是最容易想到的。
可使用什么缓存呢? Redis,Guava 的 Cache?
二、缓存过期机制
缓存过期机制有多种,列举一些我能想到的。
缓存过期机制有:
- 定时器定时更新
- 阻塞状态
- 触发时更新
(1)定时器定时更新
可以使用定时组件( 例如:quartz、xxl-job) 来定时更新,也可以起一个线程定时更新。
例如,Nacos 内部的配置动态刷新原理是通过客户端维护长轮询的任务,定时拉取发生变更的配置,然后将最新的数据推送给客户端 Listener 的持有者。
Nacos 里有 ClientWorker 来维护长轮询任务。ClientWorker 的构造函数会构造线程池,并且每隔 10ms 会发起 LongPollingRunnable 任务,如下代码所示:
public ClientWorker(final HttpAgent agent, final ConfigFilterChainManager configFilterChainManager,
final Properties properties) {
this.agent = agent;
this.configFilterChainManager = configFilterChainManager;
......
this.executor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
checkConfigInfo();
} catch (Throwable e) {
LOGGER.error("[" + agent.getName() + "] [sub-check] rotate check error", e);
}
}
}, 1L, 10L, TimeUnit.MILLISECONDS);
}
(2)阻塞状态
创建缓存时候,另开个线程
sleep在那,到时间就移除这个缓存。
public class Test {
public static volatile String cacheData = "init string";
@Test
public void test() throws InterruptedException {
System.out.println("初识值 : " + cacheData);
new Thread(() -> {
cacheData = "set new string";
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cacheData = "init string";
}).start();
for (int i = 0; i < 10; ++i) {
System.out.println("当前 : " + cacheData);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(3)触发时更新
获取数据时候,先走缓存,并判断缓存中是否过期。
意义在于:数据库可能更新了数据,这边再获取一次即可刷新。
好处:只在获取数据时判断,减少了其他组件的接入
如下代码,是简单的代码缓存:
public abstract class ExpireLessGet<T> {
private long lastRefresh;
private long expiredIn;
private T data;
/**
* Should set the data and expiredIn
*/
public abstract void load();
public long getLastRefresh() {
return lastRefresh;
}
public long getExpiredIn() {
return expiredIn;
}
public void setExpiredIn(long expiredIn) {
this.expiredIn = expiredIn;
}
public T getData() {
Date date = new Date();
// 初始化或者已过期
if (lastRefresh == 0 || date.getTime() >= lastRefresh + getExpiredIn()) {
load();
this.lastRefresh = date.getTime();
}
return data;
}
public void setData(T data) {
this.data = data;
}
}