Skip to content
/ cache Public

Java实现渐进式 kv缓存框架, 为日常开发提供一套简单易用的缓存框架 ,便于后期多级缓存开发

License

Notifications You must be signed in to change notification settings

zxlrise/cache

Repository files navigation

1、项目简介

Cache 用于实现一个可拓展的本地缓存。

有人的地方,就有江湖。

有高性能的地方,就有 cache。

Maven Central Build Status Open Source Love

创作目的

2、创作目的

  • 为日常开发提供一套简单易用的缓存框架

  • 便于后期多级缓存开发

  • 学以致用,开发一个类似于 redis 的渐进式缓存框架

3、特性

  • MVP 开发策略

  • fluent 流式编程体验,纵享丝滑

  • 支持 cache 固定大小

  • 支持自定义 map 实现策略

  • 支持 expire 过期特性

  • 支持自定义 evict 驱除策略

  • 内置 FIFO 和 LRU 驱除策略

  • 支持自定义删除监听器

  • 日志整合框架,自适应常见日志

  • 支持 load 初始化和 persist 持久化

  • RDB 和 AOF 两种模式

4、快速开始

4.1、准备

JDK1.7 及其以上版本

Maven 3.X 及其以上版本

4.2、maven 项目依赖

<dependency>
    <groupId>com.github.houbb</groupId>
    <artifactId>cache-core</artifactId>
    <version>0.0.15</version>
</dependency>

4.3、入门测试

ICache<String, String> cache = CacheBs.<String,String>newInstance()
                .size(2)
                .build();

cache.put("1", "1");
cache.put("2", "2");
cache.put("3", "3");
cache.put("4", "4");

Assert.assertEquals(2, cache.size());

默认为先进先出的策略,此时输出 keys,内容如下:

[3, 4]

4.4、引导类配置属性

CacheBs 作为缓存的引导类,支持 fluent 写法,编程更加优雅便捷。

上述配置等价于:

ICache<String, String> cache = CacheBs.<String,String>newInstance()
                .map(Maps.<String,String>hashMap())
                .evict(CacheEvicts.<String, String>fifo())
                .size(2)
                .build();

4.5、淘汰策略

目前内置了几种淘汰策略,可以直接通过 CacheEvicts 工具类创建。

策略 说明
none 没有任何淘汰策略
fifo 先进先出(默认策略)
lru 最基本的朴素 LRU 策略,性能一般
lruDoubleListMap 基于双向链表+MAP 实现的朴素 LRU,性能优于 lru
lruLinkedHashMap 基于 LinkedHashMap 实现的朴素 LRU,与 lruDoubleListMap 差不多
lru2Q 基于 LRU 2Q 的改进版 LRU 实现,命中率优于朴素LRU
lru2 基于 LRU-2 的改进版 LRU 实现,命中率优于 lru2Q

4.6、过期支持

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .size(3)
        .build();

cache.put("1", "1");
cache.put("2", "2");

cache.expire("1", 10);
Assert.assertEquals(2, cache.size());

TimeUnit.MILLISECONDS.sleep(50);
Assert.assertEquals(1, cache.size());
System.out.println(cache.keySet());

cache.expire("1", 10); 指定对应的 key 在 10ms 后过期。

5、删除监听器

5.1、说明

淘汰和过期,这些都是缓存的内部行为。

如果用户也关心的话,可以自定义删除监听器。

5.2、自定义监听器

直接实现 ICacheRemoveListener 接口即可。

public class MyRemoveListener<K,V> implements ICacheRemoveListener<K,V> {

    @Override
    public void listen(ICacheRemoveListenerContext<K, V> context) {
        System.out.println("【删除提示】可恶,我竟然被删除了!" + context.key());
    }

}

5.3、使用

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .size(1)
        .addRemoveListener(new MyRemoveListener<String, String>())
        .build();

cache.put("1", "1");
cache.put("2", "2");
  • 测试日志
【删除提示】可恶,我竟然被删除了!2

6、添加慢操作监听器

6.1、说明

redis 中会存储慢操作的相关日志信息,主要是由两个参数构成:

(1)slowlog-log-slower-than 预设阈值,它的单位是毫秒(1秒=1000000微秒)默认值是10000

(2)slowlog-max-len 最多存储多少条的慢日志记录

不过 redis 是直接存储到内存中,而且有长度限制。

根据实际工作体验,如果我们可以添加慢日志的监听,然后有对应的存储或者报警,这样更加方便问题的分析和快速反馈。

所以我们引入类似于删除的监听器。

6.2、自定义监听器

实现接口 ICacheSlowListener

这里每一个监听器都可以指定自己的慢日志阈值,便于分级处理。

public class MySlowListener implements ICacheSlowListener {

    @Override
    public void listen(ICacheSlowListenerContext context) {
        System.out.println("【慢日志】name: " + context.methodName());
    }

    @Override
    public long slowerThanMills() {
        return 0;
    }

}

6.3、使用

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

实际工作中,我们可以针对慢日志数据存储,便于后期分析。

也可以直接接入报警系统,及时反馈问题。

7、添加 load 加载器

7.1、说明

有时候我们需要在 cache 初始化的时候,添加对应的数据初始化。

后期可以从文件等地方加载数据。

7.2、实现

实现 ICacheLoad 接口即可。

public class MyCacheLoad implements ICacheLoad<String,String> {

    @Override
    public void load(ICache<String, String> cache) {
        cache.put("1", "1");
        cache.put("2", "2");
    }

}

我们在缓存初始化的时候,放入 2 个元素。

7.3、测试效果

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .load(new MyCacheLoad())
        .build();

Assert.assertEquals(2, cache.size());

8、添加 persist 持久化类

8.1、说明

如果我们只是把文件放在内存中,应用重启信息就丢失了。

有时候我们希望这些 key/value 信息可以持久化,存储到文件或者 database 中。

8.2、持久化

CachePersists.<String, String>dbJson("1.rdb") 指定将数据文件持久化到文件中。

定期执行,暂时全量持久化的间隔为 10min,后期考虑支持更多配置。

public void persistTest() throws InterruptedException {
    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);
}
  • 1.rdb

文件内容如下:

{"key":"2","value":"2"}
{"key":"1","value":"1"}

8.3、加载器

存储之后,可以使用对应的加载器读取文件内容:

ICache<String, String> cache = CacheBs.<String,String>newInstance()
        .load(CacheLoads.<String, String>dbJson("1.rdb"))
        .build();

Assert.assertEquals(2, cache.size());

开发文档

文档是对项目开发过程中遇到的一些问题的详细记录,主要是为了帮助没有基础的小伙伴快速理解这个项目。

  1. 实现固定缓存大小

  2. redis expire 过期原理

  3. 内存数据如何重启不丢失

  4. 添加监听器

  5. 过期策略的另一种实现思路

  6. redis AOF 持久化原理详解及实现

  7. 朴素 LRU 淘汰算法性能优化

  8. LRU 缓存淘汰算法如何避免缓存污染

  9. 缓存淘汰算法 LFU 最少使用频次

  10. clock时钟淘汰算法详解及实现

  11. redis expire 过期实现随机获取keys

  12. redis渐进式rehash详解

  13. 实现自己的 HashMap

  14. 实现渐进式 rehash map

About

Java实现渐进式 kv缓存框架, 为日常开发提供一套简单易用的缓存框架 ,便于后期多级缓存开发

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published