关于缓存:从零开始手写-redis四监听器的实现

2次阅读

共计 3173 个字符,预计需要花费 8 分钟才能阅读完成。

前言

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

感觉本文对你有帮忙的话,欢送点赞评论珍藏关注一波~

你的激励,是我最大的能源~

正文完
 0