前言
java 从零手写实现 redis(一)如何实现固定大小的缓存?
java 从零手写实现 redis(三)redis expire 过期原理
java 从零手写实现 redis(三)内存数据如何重启不失落?
本节,让咱们来一起学习一下如何实现相似 guava-cache 中的 removeListener 删除监听器,和相似 redis 中的慢日志监控的 slowListener。
删除监听器
阐明
咱们在两种场景下删除数据是对用户通明的:
(1)size 满了之后,进行数据淘汰。
(2)expire 过期时,革除数据。
这两个个性对用户原本应该是无感的,不过用户如果关怀的话,也能够通过增加删除监听器来获取到相干的变更信息。
实现思路
为了实现删除的监听,咱们须要找到删除的地位,而后调用监听器即可。
evict 驱除的场景
每次 put 数据时,都会校验 size 是否达到最大的限度,如果达到,则进行 evict 淘汰。
expire 过期的场景
用户指定 expire 工夫之后,回后盾异步执行刷新。
也存在惰性删除的场景。
接口定义
为了对立,咱们将所有的删除都定义对立的接口:
/**
* 删除监听器接口
*
* @author binbin.hou
* @since 0.0.6
* @param <K> key
* @param <V> value
*/
public interface ICacheRemoveListener<K,V> {
/**
* 监听
* @param context 上下文
* @since 0.0.6
*/
void listen(final ICacheRemoveListenerContext<K,V> context);
}
内置实现
零碎内置的实现如下:
public class CacheRemoveListener<K,V> implements ICacheRemoveListener<K,V> {private static final Log log = LogFactory.getLog(CacheRemoveListener.class);
@Override
public void listen(ICacheRemoveListenerContext<K, V> context) {log.debug("Remove key: {}, value: {}, type: {}",
context.key(), context.value(), context.type());
}
}
这个监听器是默认开启的,临时无奈敞开。
自定义
用户能够本人的须要,进行自定义实现:
public class MyRemoveListener<K,V> implements ICacheRemoveListener<K,V> {
@Override
public void listen(ICacheRemoveListenerContext<K, V> context) {System.out.println("【删除提醒】可恶,我居然被删除了!" + context.key());
}
}
测试
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.size(1)
.addRemoveListener(new MyRemoveListener<String, String>())
.build();
cache.put("1", "1");
cache.put("2", "2");
咱们指定 cache 的大小为 1,设置咱们自定义的删除监听器。
这里的删除监听器能够增加多个。
日志
测试日志如下:
[DEBUG] [2020-09-30 19:32:54.617] [main] [c.g.h.c.c.s.l.r.CacheRemoveListener.listen] - Remove key: 2, value: 2, type: evict【删除提醒】可恶,我居然被删除了!2
慢操作监听器
阐明
redis 中会存储慢操作的相干日志信息,次要是由两个参数形成:
(1)slowlog-log-slower-than 预设阈值, 它的单位是毫秒 (1 秒 =1000000 微秒) 默认值是 10000
(2)slowlog-max-len 最多存储多少条的慢日志记录
不过 redis 是间接存储到内存中,而且有长度限度。
依据理论工作体验,如果咱们能够增加慢日志的监听,而后有对应的存储或者报警,这样更加不便问题的剖析和疾速反馈。
所以咱们引入相似于删除的监听器。
实现思路
咱们解决所有的 cache 操作,并且记录对应的操作耗时。
如果耗时操作用户设置的工夫阈值,则调用慢操作监听器。
接口定义
为了保障接口的灵活性,每一个实现都能够定义本人的慢操作阈值,这样便于分级解决。
比方超过 100ms,用户能够抉择输入 warn 日志;超过 1s,可能影响到业务了,能够间接接入报警零碎。
public interface ICacheSlowListener {
/**
* 监听
* @param context 上下文
* @since 0.0.6
*/
void listen(final ICacheSlowListenerContext context);
/**
* 慢日志的阈值
* @return 慢日志的阈值
* @since 0.0.9
*/
long slowerThanMills();}
自定义监听器
实现接口 ICacheSlowListener
这里每一个监听器都能够指定本人的慢日志阈值,便于分级解决。
public class MySlowListener implements ICacheSlowListener {
@Override
public void listen(ICacheSlowListenerContext context) {System.out.println("【慢日志】name:" + context.methodName());
}
@Override
public long slowerThanMills() {return 0;}
}
应用
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.addSlowListener(new MySlowListener())
.build();
cache.put("1", "2");
cache.get("1");
- 测试成果
[DEBUG] [2020-09-30 17:40:11.547] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: put
[DEBUG] [2020-09-30 17:40:11.551] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: put, cost: 10ms【慢日志】name: put
[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: get
[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: get, cost: 1ms【慢日志】name: get
理论工作中,咱们能够针对慢日志数据存储,便于前期剖析。
也能够间接接入报警零碎,及时反馈问题。
小结
监听器实现起来比较简单,然而对于使用者的作用是比拟大的。
文中次要讲述了思路,实现局部因为篇幅限度,没有全副贴出来。
开源地址:https://github.com/houbb/cache
感觉本文对你有帮忙的话,欢送点赞评论珍藏关注一波~
你的激励,是我最大的能源~