从零手写实现redis(三)内存数据如何重启不丢失?
1、api
public interface ICacheLoad<K, V> {
/**
* 加载缓存信息
* @param cache 缓存
* @since 0.0.7
*/
void load(final ICache<K,V> cache);
}
为了便于后期拓展,定义 ICacheLoad 接口。
2、自定义初始化策略
public class MyCacheLoad implements ICacheLoad<String,String> {
@Override
public void load(ICache<String, String> cache) {
cache.put("1", "1");
cache.put("2", "2");
}
}
我们在初始化的时候,放入 2 个固定的信息。
3、测试
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.load(new MyCacheLoad())
.build();
Assert.assertEquals(2, cache.size());
只需要在缓存初始化的时候,指定对应的加载实现类即可。
上面先介绍初始化加载,其实已经完成了 cache 持久化的一半。
我们要做的另一件事,就是将 cache 的内容持久化到文件或者数据库,便于初始化的时候加载。
4、接口定义
public interface ICachePersist<K, V> {
/**
* 持久化缓存信息
* @param cache 缓存
* @since 0.0.7
*/
void persist(final ICache<K, V> cache);
}
为了便于灵活替换,我们定义一个持久化的接口。
5、简单实现
public class CachePersistDbJson<K,V> implements ICachePersist<K,V> {
/**
* 数据库路径
* @since 0.0.8
*/
private final String dbPath;
public CachePersistDbJson(String dbPath) {
this.dbPath = dbPath;
}
/**
* 持久化
* key长度 key+value
* 第一个空格,获取 key 的长度,然后截取
* @param cache 缓存
*/
@Override
public void persist(ICache<K, V> cache) {
Set<Map.Entry<K,V>> entrySet = cache.entrySet();
// 创建文件
FileUtil.createFile(dbPath);
// 清空文件
FileUtil.truncate(dbPath);
for(Map.Entry<K,V> entry : entrySet) {
K key = entry.getKey();
Long expireTime = cache.expire().expireTime(key);
PersistEntry<K,V> persistEntry = new PersistEntry<>();
persistEntry.setKey(key);
persistEntry.setValue(entry.getValue());
persistEntry.setExpire(expireTime);
String line = JSON.toJSONString(persistEntry);
FileUtil.write(dbPath, line, StandardOpenOption.APPEND);
}
}
}
6、定时执行
public class InnerCachePersist<K,V> {
private static final Log log = LogFactory.getLog(InnerCachePersist.class);
/**
* 缓存信息
* @since 0.0.8
*/
private final ICache<K,V> cache;
/**
* 缓存持久化策略
* @since 0.0.8
*/
private final ICachePersist<K,V> persist;
/**
* 线程执行类
* @since 0.0.3
*/
private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();
public InnerCachePersist(ICache<K, V> cache, ICachePersist<K, V> persist) {
this.cache = cache;
this.persist = persist;
// 初始化
this.init();
}
/**
* 初始化
* @since 0.0.8
*/
private void init() {
EXECUTOR_SERVICE.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
log.info("开始持久化缓存信息");
persist.persist(cache);
log.info("完成持久化缓存信息");
} catch (Exception exception) {
log.error("文件持久化异常", exception);
}
}
}, 0, 10, TimeUnit.MINUTES);
}
}
上面定义好了一种持久化的策略,但是没有提供对应的触发方式。
我们就采用对用户透明的设计方式:定时执行。
7、测试
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.load(new MyCacheLoad())
.persist(CachePersists.<String, String>dbJson("1.rdb"))
.build();
Assert.assertEquals(2, cache.size());
TimeUnit.SECONDS.sleep(5);
我们只需要在创建 cache 时,指定我们的持久化策略即可。