├── .gitignore
├── README.md
├── pom.xml
├── sivan-api
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── xin
│ └── cache
│ ├── annotation
│ └── SivanCacheInterceptor.java
│ └── api
│ ├── ISivanCache.java
│ ├── ISivanCacheContext.java
│ ├── ISivanCacheEntry.java
│ ├── ISivanCacheEvict.java
│ ├── ISivanCacheEvictContext.java
│ ├── ISivanCacheExpire.java
│ ├── ISivanCacheInterceptor.java
│ ├── ISivanCacheInterceptorContext.java
│ ├── ISivanCacheLoad.java
│ ├── ISivanCachePersist.java
│ ├── ISivanCacheRemoveListener.java
│ ├── ISivanCacheRemoveListenerContext.java
│ ├── ISivanCacheSlowListener.java
│ └── ISivanCacheSlowListenerContext.java
├── sivan-core
├── pom.xml
└── src
│ └── main
│ └── java
│ └── com
│ └── xin
│ └── cache
│ ├── bs
│ └── SivanCacheBs.java
│ ├── constant
│ └── SivanCacheRemoveType.java
│ ├── core
│ └── SivanCache.java
│ ├── exception
│ └── SivanCacheRuntimeException.java
│ ├── model
│ ├── CircleListNode.java
│ ├── DoubleListNode.java
│ ├── FreqNode.java
│ ├── PersistAofEntry.java
│ ├── PersistRdbEntry.java
│ ├── SivanCacheEntry.java
│ └── SivanCacheEvictContext.java
│ └── support
│ ├── evict
│ ├── AbstractSivanCacheEvict.java
│ ├── SivanCacheEvictClock.java
│ ├── SivanCacheEvictContext.java
│ ├── SivanCacheEvictFifo.java
│ ├── SivanCacheEvictLfu.java
│ ├── SivanCacheEvictLru.java
│ ├── SivanCacheEvictLru2.java
│ ├── SivanCacheEvictLru2Q.java
│ ├── SivanCacheEvictLruDoubleListMap.java
│ ├── SivanCacheEvictLruLinkedHashMap.java
│ ├── SivanCacheEvictNone.java
│ └── SivanCacheEvicts.java
│ ├── expire
│ ├── SivanCacheExpire.java
│ ├── SivanCacheExpireRandom.java
│ └── SivanCacheExpireSort.java
│ ├── interceptor
│ ├── SivanCacheInterceptorContext.java
│ ├── SivanCacheInterceptors.java
│ ├── aof
│ │ └── SivanCacheInterceptorAof.java
│ ├── common
│ │ └── SivanCacheInterceptorCost.java
│ ├── evict
│ │ └── SivanCacheInterceptorEvict.java
│ └── refresh
│ │ └── SivanCacheInterceptorRefresh.java
│ ├── listener
│ ├── remove
│ │ ├── SivanCacheRemoveListener.java
│ │ ├── SivanCacheRemoveListenerContext.java
│ │ └── SivanCacheRemoveListeners.java
│ └── slow
│ │ ├── SivanCacheSlowListener.java
│ │ ├── SivanCacheSlowListenerContext.java
│ │ ├── SivanCacheSlowListeners.java
│ │ └── package-info.java
│ ├── load
│ ├── SivanCacheLoadAof.java
│ ├── SivanCacheLoadDbJson.java
│ ├── SivanCacheLoadNone.java
│ └── SivanCacheLoads.java
│ ├── map
│ └── Maps.java
│ ├── persist
│ ├── InnerSivanCachePersist.java
│ ├── SivanCachePersistAdaptor.java
│ ├── SivanCachePersistAof.java
│ ├── SivanCachePersistDbJson.java
│ ├── SivanCachePersistNone.java
│ └── SivanCachePersists.java
│ ├── proxy
│ ├── ISivanCacheProxy.java
│ ├── SivanCacheProxy.java
│ ├── bs
│ │ ├── ISivanCacheProxyBsContext.java
│ │ ├── SivanCacheProxyBs.java
│ │ └── SivanCacheProxyBsContext.java
│ ├── cglib
│ │ └── CglibProxy.java
│ ├── dynamic
│ │ ├── DynamicProxy.java
│ │ └── package-info.java
│ └── none
│ │ └── NoneProxy.java
│ └── struct
│ └── lru
│ ├── ILruMap.java
│ └── impl
│ ├── LruMapCircleList.java
│ └── LruMapDoubleList.java
└── sivan-test
├── pom.xml
└── src
└── test
└── java
└── com
└── xin
└── cache
├── Test001.java
├── bs
└── SivanCacheBsTest.java
├── listener
├── MyRemoveListener.java
└── MySlowListener.java
└── load
└── MySivanCacheLoad.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | !.mvn/wrapper/maven-wrapper.jar
3 | !**/src/main/**/target/
4 | !**/src/test/**/target/
5 |
6 | ### IntelliJ IDEA ###
7 | .idea/modules.xml
8 | .idea/jarRepositories.xml
9 | .idea/compiler.xml
10 | .idea/libraries/
11 | *.iws
12 | *.iml
13 | *.ipr
14 |
15 | ### Eclipse ###
16 | .apt_generated
17 | .classpath
18 | .factorypath
19 | .project
20 | .settings
21 | .springBeans
22 | .sts4-cache
23 |
24 | ### NetBeans ###
25 | /nbproject/private/
26 | /nbbuild/
27 | /dist/
28 | /nbdist/
29 | /.nb-gradle/
30 | build/
31 | !**/src/main/**/build/
32 | !**/src/test/**/build/
33 |
34 | ### VS Code ###
35 | .vscode/
36 |
37 | ### Mac OS ###
38 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SivanCache渐进式缓存框架😀
2 |
3 | 这个项目有logo啦!
4 |
5 | 
6 |
7 | ## 项目介绍😇
8 | Java实现渐进式 kv缓存框架, 为日常开发提供一套简单易用的缓存框架 ,便于后期多级缓存开发。
9 |
10 | 截至目前已经开发的功能如下:
11 |
12 | ● 支持 expire 过期特性,实现了定时删除和惰性删除两种过期键删除策略;【√】
13 |
14 | ● 内置LRU、LRU-2、 LFU、2Q 等多种缓存淘汰算法;【√】
15 |
16 | ● 自定义删除监听器和慢操作监听器,实现了对删除和慢操作的监听;【√】
17 |
18 | ● 支持 AOF 持久化机制,实现了对AOF文件的载入与数据还原;【√】
19 |
20 | ● 自定义渐进式 rehash HashMap,实现了渐进式 rehash 扩容机制。【√】
21 |
22 | ## 项目设计思路
23 | 考虑点主要在数据用何种方式存储,能存储多少数据,多余的数据如何处理以及是否支持持久化等几个点。
24 |
25 | - 数据结构
26 | - 本地缓存最常见的是直接使用 Map 来存储,比如 guava 使用 ConcurrentHashMap,ehcache 也是用了 ConcurrentHashMap,Mybatis 二级缓存使用 HashMap 来存储:
27 | - 或者复杂的如 redis 一样提供了多种数据类型哈希,列表,集合,有序集合等,底层使用了双端链表,压缩列表,集合,跳跃表等数据结构;
28 |
29 | - 存贮上限
30 | - 因为是本地缓存,内存有上限,所以一般都会指定缓存对象的数量比如 1024,当达到某个上限后需要有某种淘汰策略去删除多余的数据;
31 |
32 | - 淘汰策略
33 | - 配合存贮上限之后使用,常见的比如有FIFO(先进先出)、 LRU(最近最少使用)、LFU(最近最不常用)、SOFT(软引用)、 LRU-K 、2Q 等策略;
34 |
35 | - 过期时间
36 | - 设置过期时间,让缓存数据在指定时间过后自动删除;常见的过期数据删除策略有两种方式:定时删除和惰性删除;
37 | - 定时删除:设置某个key的过期时间同时,我们创建一个定时器,让定时器在该过期时间到来时,立即执行对其进行删除的操作。
38 | - 惰性删除:设置该key过期时间后,我们不去管它,当需要该key时,我们在检查其是否过期,如果过期,我们就删掉它,反之返回该key。
39 |
40 | - 线程安全
41 | - 像 redis 是直接使用单线程处理,所以就不存在线程安全问题;而我们现在提供的本地缓存往往是可以多个线程同时访问的,所以线程安全是不容忽视的问题;并且线程安全问题是不应该抛给使用者去保证;
42 | - 尽量用线程安全的类去存储数据,比如使用 ConcurrentHashMap 代替 HashMap;或者提供相应的同步处理类,比如 Mybatis 提供了 SynchronizedCache:
43 |
44 | - 简明的接口
45 | - 提供常用的 get,put,remove,clear,getSize 方法即可。
46 |
47 | - 是否支持持久化
48 | - 如果我们只是把文件放在内存中,应用重启信息就丢失了。有时候我们希望这些 key/value 信息可以持久化,存储到文件或者 database 中。
49 | - 持久化的好处是重启之后可以再次加载文件中的数据,这样就起到类似热加载的功效;
50 | - 像Redis提供了两种不同的持久化方法可以将数据存储在磁盘中,一种是快照RDB,另一种是只追加文件AOF
51 |
52 | - 监听器
53 | - 根据实际工作体验,我们可以添加对删除和慢日志的监听,然后有对应的存储或者报警,这样更加方便问题的分析和快速反馈。
54 |
55 | ## TODO
56 |
57 | - 优化过期特性,看看能不能全部仿Redis;
58 | - 引入AOF + RDB双持久化策略;使用了混合持久化;
59 | - AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。重启 Cache 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。
60 | - 调用GPT3.5接口,实现AI生成功能。
61 |
62 | ## 写在最后
63 |
64 | 欢迎你来做一些贡献!如果你有更好的想法,可以联系我!
65 |
66 | ## 联系我们
67 | 邮箱:15943999893@163.com
68 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.xin
8 | Sivan_Cache
9 | 1.0-SNAPSHOT
10 | pom
11 |
12 | sivan-api
13 | sivan-core
14 | sivan-test
15 |
16 |
17 |
18 |
19 |
20 | 3.2
21 | 3.2
22 | 2.18.1
23 | false
24 | false
25 |
26 | 2.2.1
27 | 2.9.1
28 | 1.5
29 |
30 | 4.3.0
31 | 2.7
32 |
33 |
34 | 1.7
35 |
36 |
37 | 0.1.117
38 | 0.0.1
39 | 0.0.3
40 | 1.1.8
41 |
42 |
43 | 4.12
44 | 1.2.70
45 | 8
46 | 8
47 | UTF-8
48 |
49 |
50 |
51 |
52 |
53 |
54 | com.github.houbb
55 | cache-api
56 | ${project.version}
57 |
58 |
59 | com.github.houbb
60 | cache-core
61 | ${project.version}
62 |
63 |
64 |
65 |
66 | com.github.houbb
67 | log-integration
68 | ${log-integration.version}
69 |
70 |
71 | com.github.houbb
72 | heaven
73 | ${heaven.version}
74 |
75 |
76 | com.github.houbb
77 | test-core
78 | ${test.version}
79 |
80 |
81 | com.github.houbb
82 | aop-core
83 | ${aop.version}
84 |
85 |
86 |
87 |
88 | com.alibaba
89 | fastjson
90 | ${fastjson.version}
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | org.apache.maven.plugins
100 | maven-compiler-plugin
101 | ${plugin.compiler.version}
102 |
103 | ${project.compiler.level}
104 | ${project.compiler.level}
105 | ${project.build.sourceEncoding}
106 | -proc:none
107 |
108 |
109 |
110 |
111 |
112 | org.apache.maven.plugins
113 | maven-javadoc-plugin
114 | ${plugin.maven-javadoc-plugin.version}
115 |
116 |
117 |
118 |
119 |
120 | cache
121 | The cache tool for java.
122 |
123 |
124 | org.sonatype.oss
125 | oss-parent
126 | 7
127 |
128 |
129 |
130 | The Apache Software License, Version 2.0
131 | http://www.apache.org/licenses/LICENSE-2.0.txt
132 | repo
133 |
134 |
135 |
--------------------------------------------------------------------------------
/sivan-api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.xin
8 | Sivan_Cache
9 | 1.0-SNAPSHOT
10 |
11 |
12 | sivan-api
13 |
14 | 8
15 | 8
16 | UTF-8
17 |
18 |
19 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/annotation/SivanCacheInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.annotation;
2 |
3 | import java.lang.annotation.*;
4 |
5 | /**
6 | * 缓存拦截器
7 | * @author sivan
8 | */
9 | @Documented
10 | @Inherited
11 | @Target(ElementType.METHOD)
12 | @Retention(RetentionPolicy.RUNTIME)
13 | public @interface SivanCacheInterceptor {
14 |
15 | /**
16 | * 通用拦截器
17 | *
18 | * 1. 耗时统计
19 | * 2. 慢日志统计
20 | *
21 | * etc.
22 | * @return 默认开启
23 | */
24 | boolean common() default true;
25 |
26 | /**
27 | * 是否启用刷新
28 | * @return false
29 | */
30 | boolean refresh() default false;
31 |
32 | /**
33 | * 操作是否需要 append to file,默认为 false
34 | * 主要针对 cache 内容有变更的操作,不包括查询操作。
35 | * 包括删除,添加,过期等操作。
36 | * @return 是否
37 | */
38 | boolean aof() default false;
39 |
40 | /**
41 | * 是否执行驱除更新
42 | *
43 | * 主要用于 LRU/LFU 等驱除策略
44 | * @return 是否
45 | */
46 | boolean evict() default false;
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCache.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 | import com.xin.cache.api.ISivanCacheEvict;
3 | import com.xin.cache.api.ISivanCacheRemoveListener;
4 |
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * 缓存接口
10 | * @author sivan
11 | */
12 | public interface ISivanCache extends Map {
13 |
14 | /**
15 | * 设置过期时间
16 | * (1)如果 key 不存在,则什么都不做。
17 | * (2)暂时不提供新建 key 指定过期时间的方式,会破坏原来的方法。
18 | *
19 | * 会做什么:
20 | * 类似于 redis
21 | * (1)惰性删除。
22 | * 在执行下面的方法时,如果过期则进行删除。
23 | * {@link ISivanCache#get(Object)} 获取
24 | * {@link ISivanCache#values()} 获取所有值
25 | * {@link ISivanCache#entrySet()} 获取所有明细
26 | *
27 | * 【数据的不一致性】
28 | * 调用其他方法,可能得到的不是使用者的预期结果,因为此时的 expire 信息可能没有被及时更新。
29 | * 比如
30 | * {@link ISivanCache#isEmpty()} 是否为空
31 | * {@link ISivanCache#size()} 当前大小
32 | * 同时会导致以 size() 作为过期条件的问题。
33 | *
34 | * 解决方案:考虑添加 refresh 等方法,暂时不做一致性的考虑。
35 | * 对于实际的使用,我们更关心 K/V 的信息。
36 | *
37 | * (2)定时删除
38 | * 启动一个定时任务。每次随机选择指定大小的 key 进行是否过期判断。
39 | * 类似于 redis,为了简化,可以考虑设定超时时间,频率与超时时间成反比。
40 | *
41 | * 其他拓展性考虑:
42 | * 后期考虑提供原子性操作,保证事务性。暂时不做考虑。
43 | * 此处默认使用 TTL 作为比较的基准,暂时不想支持 LastAccessTime 的淘汰策略。会增加复杂度。
44 | * 如果增加 lastAccessTime 过期,本方法可以不做修改。
45 | *
46 | * @param key key
47 | * @param timeInMills 毫秒时间之后过期
48 | * @return this
49 | */
50 | ISivanCache expire(final K key, final long timeInMills);
51 |
52 | /**
53 | * 在指定的时间过期
54 | * @param key key
55 | * @param timeInMills 时间戳
56 | * @return this
57 | */
58 | ISivanCache expireAt(final K key, final long timeInMills);
59 |
60 | /**
61 | * 获取缓存的过期处理类
62 | * @return 处理类实现
63 | */
64 | ISivanCacheExpire expire();
65 |
66 | /**
67 | * 删除监听类列表
68 | * @return 监听器列表
69 | */
70 | List> removeListeners();
71 |
72 | /**
73 | * 慢日志监听类列表
74 | * @return 监听器列表
75 | */
76 | List slowListeners();
77 |
78 | /**
79 | * 加载信息
80 | * @return 加载信息
81 | */
82 | ISivanCacheLoad load();
83 |
84 | /**
85 | * 持久化类
86 | * @return 持久化类
87 | */
88 | ISivanCachePersist persist();
89 |
90 | /**
91 | * 淘汰策略
92 | * @return 淘汰
93 | */
94 | ISivanCacheEvict evict();
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * 缓存上下文
7 | */
8 | public interface ISivanCacheContext {
9 |
10 | /**
11 | * map 信息
12 | * @return map
13 | */
14 | Map map();
15 |
16 | /**
17 | * 大小限制
18 | * @return 大小限制
19 | */
20 | int size();
21 |
22 | /**
23 | * 驱除策略
24 | * @return 策略
25 | */
26 | ISivanCacheEvict cacheEvict();
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheEntry.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | public interface ISivanCacheEntry {
4 |
5 | K key();
6 | V value();
7 |
8 | }
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheEvict.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 淘汰策略接口
5 | */
6 | public interface ISivanCacheEvict {
7 | /**
8 | * 淘汰策略
9 | * @param context
10 | * @return
11 | */
12 | ISivanCacheEntry evict(ISivanCacheEvictContext context);
13 |
14 | /**
15 | * 更新key信息
16 | * @param key
17 | */
18 | void updateKey(K key);
19 |
20 | /**
21 | * 删除key信息
22 | * @param key
23 | */
24 | void removeKey(K key);
25 | }
26 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheEvictContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 驱逐策略上下文
5 | * @param
6 | * @param
7 | */
8 | public interface ISivanCacheEvictContext {
9 |
10 | /**
11 | * 新加的 key
12 | * @return key
13 | */
14 | K key();
15 |
16 | /**
17 | * cache 实现
18 | * @return map
19 | */
20 | ISivanCache cache();
21 |
22 | /**
23 | * 获取大小
24 | * @return 大小
25 | */
26 | int size();
27 |
28 | }
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheExpire.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 |
6 | /**
7 | * 缓存过期接口
8 | * @author sivan
9 | */
10 | public interface ISivanCacheExpire {
11 |
12 | /**
13 | * 指定过期信息
14 | * @param key key
15 | * @param expireAt 什么时候过期
16 | */
17 | void expire(final K key, final long expireAt);
18 |
19 | /**
20 | * 惰性删除中需要处理的 keys
21 | * @param keyList keys
22 | */
23 | void refreshExpire(final Collection keyList);
24 |
25 | /**
26 | * 待过期的 key
27 | * 不存在,则返回 null
28 | * @param key 待过期的 key
29 | * @return 结果
30 | */
31 | Long expireTime(final K key);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 拦截器接口
5 | *
6 | * (1)耗时统计
7 | * (2)监听器
8 | *
9 | * @author sivan
10 | * @param key
11 | * @param value
12 | */
13 | public interface ISivanCacheInterceptor {
14 |
15 | /**
16 | * 方法执行之前
17 | * @param context 上下文
18 | */
19 | void before(ISivanCacheInterceptorContext context);
20 |
21 | /**
22 | * 方法执行之后
23 | * @param context 上下文
24 | */
25 | void after(ISivanCacheInterceptorContext context);
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheInterceptorContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | import java.lang.reflect.Method;
4 |
5 | /**
6 | * 拦截器上下文接口
7 | *
8 | * (1)get
9 | * (2)put
10 | * (3)remove
11 | * (4)expire
12 | * (5)evict
13 | *
14 | * @author sivan
15 | * @param key
16 | * @param value
17 | */
18 | public interface ISivanCacheInterceptorContext {
19 |
20 | /**
21 | * 缓存信息
22 | * @return 缓存信息
23 | */
24 | ISivanCache cache();
25 |
26 | /**
27 | * 执行的方法信息
28 | * @return 方法
29 | */
30 | Method method();
31 |
32 | /**
33 | * 执行的参数
34 | * @return 参数
35 | */
36 | Object[] params();
37 |
38 | /**
39 | * 方法执行的结果
40 | * @return 结果
41 | */
42 | Object result();
43 |
44 | /**
45 | * 开始时间
46 | * @return 时间
47 | */
48 | long startMills();
49 |
50 | /**
51 | * 结束时间
52 | * @return 时间
53 | */
54 | long endMills();
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheLoad.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 缓存接口
5 | * @author sivan
6 | * @param key
7 | * @param value
8 | */
9 | public interface ISivanCacheLoad {
10 |
11 | /**
12 | * 加载缓存信息
13 | * @param cache 缓存
14 | */
15 | void load(final ISivanCache cache);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCachePersist.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | import java.util.concurrent.TimeUnit;
4 |
5 | /**
6 | * 持久化缓存接口
7 | * @author sivan
8 | * @param key
9 | * @param value
10 | */
11 | public interface ISivanCachePersist {
12 |
13 | /**
14 | * 持久化缓存信息
15 | * @param cache 缓存
16 | */
17 | void persist(final ISivanCache cache);
18 |
19 | /**
20 | * 延迟时间
21 | * @return 延迟
22 | */
23 | long delay();
24 |
25 | /**
26 | * 时间间隔
27 | * @return 间隔
28 | */
29 | long period();
30 |
31 | /**
32 | * 时间单位
33 | * @return 时间单位
34 | */
35 | TimeUnit timeUnit();
36 | }
37 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheRemoveListener.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 删除监听器接口
5 | *
6 | * @author sivan
7 | * @param key
8 | * @param value
9 | */
10 | public interface ISivanCacheRemoveListener {
11 |
12 | /**
13 | * 监听
14 | * @param context 上下文
15 | */
16 | void listen(final ISivanCacheRemoveListenerContext context);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheRemoveListenerContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 缓存删除 监听器 上下文
5 | */
6 | public interface ISivanCacheRemoveListenerContext {
7 |
8 | /**
9 | * 要删除的key
10 | * @return
11 | */
12 | K key();
13 |
14 | /**
15 | * 要删除的value
16 | * @return
17 | */
18 | V value();
19 |
20 | /**
21 | * 删除类型
22 | * @return
23 | */
24 | String type();
25 | }
26 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheSlowListener.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 慢日志操作接口
5 | *
6 | * @author sivan
7 | */
8 | public interface ISivanCacheSlowListener {
9 |
10 | /**
11 | * 监听
12 | * @param context 上下文
13 | */
14 | void listen(final ISivanCacheSlowListenerContext context);
15 |
16 | /**
17 | * 慢日志的阈值
18 | * @return 慢日志的阈值
19 | */
20 | long slowerThanMills();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/sivan-api/src/main/java/com/xin/cache/api/ISivanCacheSlowListenerContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.api;
2 |
3 | /**
4 | * 慢日志监听器上下文
5 | *
6 | * @author sivan
7 | */
8 | public interface ISivanCacheSlowListenerContext {
9 |
10 | /**
11 | * 方法名称
12 | * @return 方法名称
13 | */
14 | String methodName();
15 |
16 | /**
17 | * 参数信息
18 | * @return 参数列表
19 | */
20 | Object[] params();
21 |
22 | /**
23 | * 方法结果
24 | * @return 方法结果
25 | */
26 | Object result();
27 |
28 | /**
29 | * 开始时间
30 | * @return 时间
31 | */
32 | long startTimeMills();
33 |
34 | /**
35 | * 结束时间
36 | * @return 结束时间
37 | */
38 | long endTimeMills();
39 |
40 | /**
41 | * 消耗时间
42 | * @return 耗时
43 | */
44 | long costTimeMills();
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/sivan-core/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.xin
8 | Sivan_Cache
9 | 1.0-SNAPSHOT
10 |
11 |
12 | sivan-core
13 |
14 |
15 |
16 |
17 | com.github.houbb
18 | log-integration
19 |
20 |
21 | com.github.houbb
22 | heaven
23 |
24 |
25 | com.github.houbb
26 | aop-core
27 |
28 |
29 |
30 |
31 | com.alibaba
32 | fastjson
33 |
34 |
35 | org.projectlombok
36 | lombok
37 | 1.18.28
38 | compile
39 |
40 |
41 | com.xin
42 | sivan-api
43 | 1.0-SNAPSHOT
44 | compile
45 |
46 |
47 |
48 |
49 | 8
50 | 8
51 | UTF-8
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/bs/SivanCacheBs.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.bs;
2 |
3 | import com.github.houbb.heaven.util.common.ArgUtil;
4 | import com.xin.cache.api.*;
5 | import com.xin.cache.core.SivanCache;
6 | import com.xin.cache.support.evict.SivanCacheEvicts;
7 | import com.xin.cache.support.listener.remove.SivanCacheRemoveListeners;
8 | import com.xin.cache.support.listener.slow.SivanCacheSlowListeners;
9 | import com.xin.cache.support.load.SivanCacheLoads;
10 | import com.xin.cache.support.persist.SivanCachePersists;
11 | import com.xin.cache.support.proxy.SivanCacheProxy;
12 |
13 | import java.util.HashMap;
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 | /**
18 | * 缓存引导类
19 | * @author sivan
20 | *
21 | */
22 | public final class SivanCacheBs {
23 |
24 | private SivanCacheBs(){}
25 |
26 | /**
27 | * 创建对象实例
28 | * @param key
29 | * @param value
30 | * @return this
31 | *
32 | */
33 | public static SivanCacheBs newInstance() {
34 | return new SivanCacheBs<>();
35 | }
36 |
37 | /**
38 | * map 实现
39 | *
40 | */
41 | private Map map = new HashMap<>();
42 |
43 | /**
44 | * 大小限制
45 | *
46 | */
47 | private int size = Integer.MAX_VALUE;
48 |
49 | /**
50 | * 驱除策略
51 | *
52 | */
53 | private ISivanCacheEvict evict = SivanCacheEvicts.fifo();
54 |
55 | /**
56 | * 删除监听类
57 | *
58 | */
59 | private final List> removeListeners = SivanCacheRemoveListeners.defaults();
60 |
61 | /**
62 | * 慢操作监听类
63 | *
64 | */
65 | private final List slowListeners = SivanCacheSlowListeners.none();
66 |
67 | /**
68 | * 加载策略
69 | *
70 | */
71 | private ISivanCacheLoad load = SivanCacheLoads.none();
72 |
73 | /**
74 | * 持久化实现策略
75 | *
76 | */
77 | private ISivanCachePersist persist = SivanCachePersists.none();
78 |
79 | /**
80 | * map 实现
81 | * @param map map
82 | * @return this
83 | *
84 | */
85 | public SivanCacheBs map(Map map) {
86 | ArgUtil.notNull(map, "map");
87 |
88 | this.map = map;
89 | return this;
90 | }
91 |
92 | /**
93 | * 设置 size 信息
94 | * @param size size
95 | * @return this
96 | *
97 | */
98 | public SivanCacheBs size(int size) {
99 | ArgUtil.notNegative(size, "size");
100 |
101 | this.size = size;
102 | return this;
103 | }
104 |
105 | /**
106 | * 设置驱除策略
107 | * @param evict 驱除策略
108 | * @return this
109 | *
110 | */
111 | public SivanCacheBs evict(ISivanCacheEvict evict) {
112 | ArgUtil.notNull(evict, "evict");
113 |
114 | this.evict = evict;
115 | return this;
116 | }
117 |
118 | /**
119 | * 设置加载
120 | * @param load 加载
121 | * @return this
122 | *
123 | */
124 | public SivanCacheBs load(ISivanCacheLoad load) {
125 | ArgUtil.notNull(load, "load");
126 |
127 | this.load = load;
128 | return this;
129 | }
130 |
131 | /**
132 | * 添加删除监听器
133 | * @param removeListener 监听器
134 | * @return this
135 | *
136 | */
137 | public SivanCacheBs addRemoveListener(ISivanCacheRemoveListener removeListener) {
138 | ArgUtil.notNull(removeListener, "removeListener");
139 |
140 | this.removeListeners.add(removeListener);
141 | return this;
142 | }
143 |
144 | /**
145 | * 添加慢日志监听器
146 | * @param slowListener 监听器
147 | * @return this
148 | *
149 | */
150 | public SivanCacheBs addSlowListener(ISivanCacheSlowListener slowListener) {
151 | ArgUtil.notNull(slowListener, "slowListener");
152 |
153 | this.slowListeners.add(slowListener);
154 | return this;
155 | }
156 |
157 | /**
158 | * 设置持久化策略
159 | * @param persist 持久化
160 | * @return this
161 | *
162 | */
163 | public SivanCacheBs persist(ISivanCachePersist persist) {
164 | this.persist = persist;
165 | return this;
166 | }
167 |
168 | /**
169 | * 构建缓存信息
170 | * @return 缓存信息
171 | *
172 | */
173 | public ISivanCache build() {
174 | SivanCache cache = new SivanCache<>();
175 | cache.map(map);
176 | cache.evict(evict);
177 | cache.sizeLimit(size);
178 | cache.removeListeners(removeListeners);
179 | cache.load(load);
180 | cache.persist(persist);
181 | cache.slowListeners(slowListeners);
182 |
183 | // 初始化
184 | cache.init();
185 | return SivanCacheProxy.getProxy(cache);
186 | }
187 |
188 | }
189 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/constant/SivanCacheRemoveType.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.constant;
2 |
3 | /**
4 | * 删除类型
5 | */
6 | public enum SivanCacheRemoveType {
7 | EXPIRE("expire", "过期"),
8 | EVICT("evict", "淘汰"),
9 | ;
10 |
11 | private final String code;
12 |
13 | private final String desc;
14 |
15 |
16 | SivanCacheRemoveType(String code, String desc) {
17 | this.code = code;
18 | this.desc = desc;
19 | }
20 |
21 | public String code() {
22 | return code;
23 | }
24 |
25 | public String desc() {
26 | return desc;
27 | }
28 |
29 | @Override
30 | public String toString() {
31 | return "CacheRemoveType{" +
32 | "code='" + code + '\'' +
33 | ", desc='" + desc + '\'' +
34 | '}';
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/core/SivanCache.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.core;
2 |
3 | import com.github.houbb.heaven.util.lang.ObjectUtil;
4 | import com.xin.cache.annotation.SivanCacheInterceptor;
5 | import com.xin.cache.api.*;
6 | import com.xin.cache.constant.SivanCacheRemoveType;
7 | import com.xin.cache.exception.SivanCacheRuntimeException;
8 | import com.xin.cache.model.SivanCacheEvictContext;
9 | import com.xin.cache.support.expire.SivanCacheExpire;
10 | import com.xin.cache.support.listener.remove.SivanCacheRemoveListenerContext;
11 | import com.xin.cache.support.persist.InnerSivanCachePersist;
12 | import com.xin.cache.support.proxy.SivanCacheProxy;
13 |
14 | import java.util.*;
15 |
16 | /**
17 | * 缓存信息
18 | *
19 | * @author sivan
20 | * @param key
21 | * @param value
22 | *
23 | */
24 | public class SivanCache implements ISivanCache {
25 |
26 | /**
27 | * map 信息
28 | *
29 | */
30 | private Map map;
31 |
32 | /**
33 | * 大小限制
34 | *
35 | */
36 | private int sizeLimit;
37 |
38 | /**
39 | * 驱除策略
40 | *
41 | */
42 | private ISivanCacheEvict evict;
43 |
44 | /**
45 | * 过期策略
46 | * 暂时不做暴露
47 | *
48 | */
49 | private ISivanCacheExpire expire;
50 |
51 | /**
52 | * 删除监听类
53 | *
54 | */
55 | private List> removeListeners;
56 |
57 | /**
58 | * 慢日志监听类
59 | *
60 | */
61 | private List slowListeners;
62 |
63 | /**
64 | * 加载类
65 | *
66 | */
67 | private ISivanCacheLoad load;
68 |
69 | /**
70 | * 持久化
71 | *
72 | */
73 | private ISivanCachePersist persist;
74 |
75 | /**
76 | * 设置 map 实现
77 | * @param map 实现
78 | * @return this
79 | */
80 | public SivanCache map(Map map) {
81 | this.map = map;
82 | return this;
83 | }
84 |
85 | /**
86 | * 设置大小限制
87 | * @param sizeLimit 大小限制
88 | * @return this
89 | */
90 | public SivanCache sizeLimit(int sizeLimit) {
91 | this.sizeLimit = sizeLimit;
92 | return this;
93 | }
94 |
95 | /**
96 | * 设置驱除策略
97 | * @param cacheEvict 驱除策略
98 | * @return this
99 | *
100 | */
101 | public SivanCache evict(ISivanCacheEvict cacheEvict) {
102 | this.evict = cacheEvict;
103 | return this;
104 | }
105 |
106 | /**
107 | * 获取持久化类
108 | * @return 持久化类
109 | *
110 | */
111 | @Override
112 | public ISivanCachePersist persist() {
113 | return persist;
114 | }
115 |
116 |
117 | /**
118 | * 获取驱除策略
119 | * @return 驱除策略
120 | *
121 | */
122 | @Override
123 | public ISivanCacheEvict evict() {
124 | return this.evict;
125 | }
126 |
127 | /**
128 | * 设置持久化策略
129 | * @param persist 持久化
130 | *
131 | */
132 | public void persist(ISivanCachePersist persist) {
133 | this.persist = persist;
134 | }
135 |
136 | @Override
137 | public List> removeListeners() {
138 | return removeListeners;
139 | }
140 |
141 | public SivanCache removeListeners(List> removeListeners) {
142 | this.removeListeners = removeListeners;
143 | return this;
144 | }
145 |
146 |
147 | @Override
148 | public List slowListeners() {
149 | return slowListeners;
150 | }
151 |
152 | public SivanCache slowListeners(List slowListeners) {
153 | this.slowListeners = slowListeners;
154 | return this;
155 | }
156 |
157 | @Override
158 | public ISivanCacheLoad load() {
159 | return load;
160 | }
161 |
162 | public SivanCache load(ISivanCacheLoad load) {
163 | this.load = load;
164 | return this;
165 | }
166 |
167 | /**
168 | * 初始化
169 | *
170 | */
171 | public void init() {
172 | this.expire = new SivanCacheExpire<>(this);
173 | this.load.load(this);
174 |
175 | // 初始化持久化
176 | if(this.persist != null) {
177 | new InnerSivanCachePersist<>(this, persist);
178 | }
179 | }
180 |
181 | /**
182 | * 设置过期时间
183 | * @param key key
184 | * @param timeInMills 毫秒时间之后过期
185 | * @return this
186 | */
187 | @Override
188 | @SivanCacheInterceptor
189 | public ISivanCache expire(K key, long timeInMills) {
190 | long expireTime = System.currentTimeMillis() + timeInMills;
191 |
192 | // 使用代理调用
193 | SivanCache cachePoxy = (SivanCache) SivanCacheProxy.getProxy(this);
194 | return cachePoxy.expireAt(key, expireTime);
195 | }
196 |
197 | /**
198 | * 指定过期信息
199 | * @param key key
200 | * @param timeInMills 时间戳
201 | * @return this
202 | */
203 | @Override
204 | @SivanCacheInterceptor(aof = true)
205 | public ISivanCache expireAt(K key, long timeInMills) {
206 | this.expire.expire(key, timeInMills);
207 | return this;
208 | }
209 |
210 | @Override
211 | @SivanCacheInterceptor
212 | public ISivanCacheExpire expire() {
213 | return this.expire;
214 | }
215 |
216 | @Override
217 | @SivanCacheInterceptor(refresh = true)
218 | public int size() {
219 | return map.size();
220 | }
221 |
222 | @Override
223 | @SivanCacheInterceptor(refresh = true)
224 | public boolean isEmpty() {
225 | return map.isEmpty();
226 | }
227 |
228 | @Override
229 | @SivanCacheInterceptor(refresh = true, evict = true)
230 | public boolean containsKey(Object key) {
231 | return map.containsKey(key);
232 | }
233 |
234 | @Override
235 | @SivanCacheInterceptor(refresh = true)
236 | public boolean containsValue(Object value) {
237 | return map.containsValue(value);
238 | }
239 |
240 | @Override
241 | @SivanCacheInterceptor(evict = true)
242 | @SuppressWarnings("unchecked")
243 | public V get(Object key) {
244 | //1. 刷新所有过期信息
245 | K genericKey = (K) key;
246 | this.expire.refreshExpire(Collections.singletonList(genericKey));
247 |
248 | return map.get(key);
249 | }
250 |
251 | @Override
252 | @SivanCacheInterceptor(aof = true, evict = true)
253 | public V put(K key, V value) {
254 | //1.1 尝试驱除
255 | SivanCacheEvictContext context = new SivanCacheEvictContext<>();
256 | context.key(key).size(sizeLimit).cache(this);
257 |
258 | ISivanCacheEntry evictEntry = evict.evict(context);
259 |
260 | // 添加拦截器调用
261 | if(ObjectUtil.isNotNull(evictEntry)) {
262 | // 执行淘汰监听器
263 | ISivanCacheRemoveListenerContext removeListenerContext = SivanCacheRemoveListenerContext.newInstance().key(evictEntry.key())
264 | .value(evictEntry.value())
265 | .type(SivanCacheRemoveType.EVICT.code());
266 | for(ISivanCacheRemoveListener listener : context.cache().removeListeners()) {
267 | listener.listen(removeListenerContext);
268 | }
269 | }
270 |
271 | //2. 判断驱除后的信息
272 | if(isSizeLimit()) {
273 | throw new SivanCacheRuntimeException("当前队列已满,数据添加失败!");
274 | }
275 |
276 | //3. 执行添加
277 | return map.put(key, value);
278 | }
279 |
280 | /**
281 | * 是否已经达到大小最大的限制
282 | * @return 是否限制
283 | *
284 | */
285 | private boolean isSizeLimit() {
286 | final int currentSize = this.size();
287 | return currentSize >= this.sizeLimit;
288 | }
289 |
290 | @Override
291 | @SivanCacheInterceptor(aof = true, evict = true)
292 | public V remove(Object key) {
293 | return map.remove(key);
294 | }
295 |
296 | @Override
297 | @SivanCacheInterceptor(aof = true)
298 | public void putAll(Map extends K, ? extends V> m) {
299 | map.putAll(m);
300 | }
301 |
302 | @Override
303 | @SivanCacheInterceptor(refresh = true, aof = true)
304 | public void clear() {
305 | map.clear();
306 | }
307 |
308 | @Override
309 | @SivanCacheInterceptor(refresh = true)
310 | public Set keySet() {
311 | return map.keySet();
312 | }
313 |
314 | @Override
315 | @SivanCacheInterceptor(refresh = true)
316 | public Collection values() {
317 | return map.values();
318 | }
319 |
320 | @Override
321 | @SivanCacheInterceptor(refresh = true)
322 | public Set> entrySet() {
323 | return map.entrySet();
324 | }
325 |
326 | }
327 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/exception/SivanCacheRuntimeException.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.exception;
2 |
3 | /**
4 | * 缓存运行时异常
5 | * @author sivan
6 | *
7 | */
8 | public class SivanCacheRuntimeException extends RuntimeException {
9 |
10 | public SivanCacheRuntimeException() {
11 | }
12 |
13 | public SivanCacheRuntimeException(String message) {
14 | super(message);
15 | }
16 |
17 | public SivanCacheRuntimeException(String message, Throwable cause) {
18 | super(message, cause);
19 | }
20 |
21 | public SivanCacheRuntimeException(Throwable cause) {
22 | super(cause);
23 | }
24 |
25 | public SivanCacheRuntimeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
26 | super(message, cause, enableSuppression, writableStackTrace);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/CircleListNode.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | /**
4 | * 循环链表节点
5 | * @author sivan
6 | * @param key
7 | * @param value
8 | */
9 | public class CircleListNode {
10 |
11 | /**
12 | * 键
13 | */
14 | private K key;
15 |
16 | /**
17 | * 值
18 | */
19 | private V value = null;
20 |
21 | /**
22 | * 是否被访问过
23 | */
24 | private boolean accessFlag = false;
25 |
26 | /**
27 | * 后一个节点
28 | */
29 | private CircleListNode pre;
30 |
31 | /**
32 | * 后一个节点
33 | */
34 | private CircleListNode next;
35 |
36 | public CircleListNode(K key) {
37 | this.key = key;
38 | }
39 |
40 | public K key() {
41 | return key;
42 | }
43 |
44 | public CircleListNode key(K key) {
45 | this.key = key;
46 | return this;
47 | }
48 |
49 | public V value() {
50 | return value;
51 | }
52 |
53 | public CircleListNode value(V value) {
54 | this.value = value;
55 | return this;
56 | }
57 |
58 | public boolean accessFlag() {
59 | return accessFlag;
60 | }
61 |
62 | public CircleListNode accessFlag(boolean accessFlag) {
63 | this.accessFlag = accessFlag;
64 | return this;
65 | }
66 |
67 | public CircleListNode pre() {
68 | return pre;
69 | }
70 |
71 | public CircleListNode pre(CircleListNode pre) {
72 | this.pre = pre;
73 | return this;
74 | }
75 |
76 | public CircleListNode next() {
77 | return next;
78 | }
79 |
80 | public CircleListNode next(CircleListNode next) {
81 | this.next = next;
82 | return this;
83 | }
84 |
85 | @Override
86 | public String toString() {
87 | return "CircleListNode{" +
88 | "key=" + key +
89 | ", value=" + value +
90 | ", accessFlag=" + accessFlag +
91 | ", pre=" + pre +
92 | ", next=" + next +
93 | '}';
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/DoubleListNode.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | /**
4 | * 双向链表节点
5 | * @author sivan
6 | * @param key
7 | * @param value
8 | */
9 | public class DoubleListNode {
10 |
11 | /**
12 | * 键
13 | */
14 | private K key;
15 |
16 | /**
17 | * 值
18 | */
19 | private V value;
20 |
21 | /**
22 | * 前一个节点
23 | */
24 | private DoubleListNode pre;
25 |
26 | /**
27 | * 后一个节点
28 | */
29 | private DoubleListNode next;
30 |
31 | public K key() {
32 | return key;
33 | }
34 |
35 | public DoubleListNode key(K key) {
36 | this.key = key;
37 | return this;
38 | }
39 |
40 | public V value() {
41 | return value;
42 | }
43 |
44 | public DoubleListNode value(V value) {
45 | this.value = value;
46 | return this;
47 | }
48 |
49 | public DoubleListNode pre() {
50 | return pre;
51 | }
52 |
53 | public DoubleListNode pre(DoubleListNode pre) {
54 | this.pre = pre;
55 | return this;
56 | }
57 |
58 | public DoubleListNode next() {
59 | return next;
60 | }
61 |
62 | public DoubleListNode next(DoubleListNode next) {
63 | this.next = next;
64 | return this;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/FreqNode.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | import java.util.Objects;
4 |
5 | /**
6 | * 包含频率信息的节点
7 | * @author sivan
8 | */
9 | public class FreqNode {
10 |
11 | /**
12 | * 键
13 | */
14 | private K key;
15 |
16 | /**
17 | * 值
18 | */
19 | private V value = null;
20 |
21 | /**
22 | * 频率
23 | */
24 | private int frequency = 1;
25 |
26 | public FreqNode(K key) {
27 | this.key = key;
28 | }
29 |
30 | public K key() {
31 | return key;
32 | }
33 |
34 | public FreqNode key(K key) {
35 | this.key = key;
36 | return this;
37 | }
38 |
39 | public V value() {
40 | return value;
41 | }
42 |
43 | public FreqNode value(V value) {
44 | this.value = value;
45 | return this;
46 | }
47 |
48 | public int frequency() {
49 | return frequency;
50 | }
51 |
52 | public FreqNode frequency(int frequency) {
53 | this.frequency = frequency;
54 | return this;
55 | }
56 |
57 | @Override
58 | public String toString() {
59 | return "FreqNode{" +
60 | "key=" + key +
61 | ", value=" + value +
62 | ", frequency=" + frequency +
63 | '}';
64 | }
65 |
66 | @Override
67 | public boolean equals(Object o) {
68 | if (this == o) {
69 | return true;
70 | }
71 | if (o == null || getClass() != o.getClass()) {
72 | return false;
73 | }
74 | FreqNode, ?> freqNode = (FreqNode, ?>) o;
75 | return frequency == freqNode.frequency &&
76 | Objects.equals(key, freqNode.key) &&
77 | Objects.equals(value, freqNode.value);
78 | }
79 |
80 | @Override
81 | public int hashCode() {
82 | return Objects.hash(key, value, frequency);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/PersistAofEntry.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | import java.util.Arrays;
4 |
5 | /**
6 | * AOF 持久化明细
7 | * @author sivan
8 | */
9 | public class PersistAofEntry {
10 |
11 | /**
12 | * 参数信息
13 | */
14 | private Object[] params;
15 |
16 | /**
17 | * 方法名称
18 | */
19 | private String methodName;
20 |
21 | /**
22 | * 新建对象实例
23 | * @return this
24 | */
25 | public static PersistAofEntry newInstance() {
26 | return new PersistAofEntry();
27 | }
28 |
29 | public Object[] getParams() {
30 | return params;
31 | }
32 |
33 | public void setParams(Object[] params) {
34 | this.params = params;
35 | }
36 |
37 | public String getMethodName() {
38 | return methodName;
39 | }
40 |
41 | public void setMethodName(String methodName) {
42 | this.methodName = methodName;
43 | }
44 |
45 | @Override
46 | public String toString() {
47 | return "PersistAofEntry{" +
48 | "params=" + Arrays.toString(params) +
49 | ", methodName='" + methodName + '\'' +
50 | '}';
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/PersistRdbEntry.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | /**
4 | * 持久化明细
5 | * @author sivan
6 | */
7 | public class PersistRdbEntry {
8 |
9 | /**
10 | * key
11 | */
12 | private K key;
13 |
14 | /**
15 | * value
16 | */
17 | private V value;
18 |
19 | /**
20 | * expire
21 | */
22 | private Long expire;
23 |
24 | public K getKey() {
25 | return key;
26 | }
27 |
28 | public void setKey(K key) {
29 | this.key = key;
30 | }
31 |
32 | public V getValue() {
33 | return value;
34 | }
35 |
36 | public void setValue(V value) {
37 | this.value = value;
38 | }
39 |
40 | public Long getExpire() {
41 | return expire;
42 | }
43 |
44 | public void setExpire(Long expire) {
45 | this.expire = expire;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/SivanCacheEntry.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | import com.alibaba.fastjson.annotation.JSONField;
4 | import com.xin.cache.api.ISivanCacheEntry;
5 | import com.xin.cache.api.ISivanCacheEvictContext;
6 |
7 | public class SivanCacheEntry implements ISivanCacheEntry {
8 |
9 | /**
10 | * key
11 | * 1
12 | */
13 | @JSONField(ordinal = 1)
14 | private final K key;
15 |
16 | /**
17 | * value
18 | * 1
19 | */
20 | @JSONField(ordinal = 2)
21 | private final V value;
22 |
23 | /**
24 | * 新建元素
25 | * @param key key
26 | * @param value value
27 | * @param 泛型
28 | * @param 泛型
29 | * @return 结果
30 | * 1
31 | */
32 | public static SivanCacheEntry of(final K key,
33 | final V value) {
34 | return new SivanCacheEntry<>(key, value);
35 | }
36 |
37 | public SivanCacheEntry(K key, V value) {
38 | this.key = key;
39 | this.value = value;
40 | }
41 |
42 | @Override
43 | public K key() {
44 | return key;
45 | }
46 |
47 | @Override
48 | public V value() {
49 | return value;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "EvictEntry{" +
55 | "key=" + key +
56 | ", value=" + value +
57 | '}';
58 |
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/model/SivanCacheEvictContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.model;
2 |
3 | import com.xin.cache.api.ISivanCache;
4 | import com.xin.cache.api.ISivanCacheEvictContext;
5 |
6 | /**
7 | * @Author 不知名网友鑫
8 | * @Date 2023/11/6
9 | **/
10 | public class SivanCacheEvictContext implements ISivanCacheEvictContext {
11 |
12 | private int size;
13 | private K key;
14 | private V value;
15 | private ISivanCache cache;
16 |
17 | public SivanCacheEvictContext key(K key) {
18 | this.key = key;
19 | return this;
20 | }
21 | public SivanCacheEvictContext size(int size) {
22 | this.size = size;
23 | return this;
24 | }
25 | public SivanCacheEvictContext cache(ISivanCache cache) {
26 | this.cache = cache;
27 | return this;
28 | }
29 |
30 | @Override
31 | public K key() {
32 | return key;
33 | }
34 |
35 | @Override
36 | public ISivanCache cache() {
37 | return cache;
38 | }
39 |
40 | @Override
41 | public int size() {
42 | return size;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/AbstractSivanCacheEvict.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.xin.cache.api.ISivanCacheEntry;
5 | import com.xin.cache.api.ISivanCacheEvict;
6 | import com.xin.cache.api.ISivanCacheEvictContext;
7 |
8 | /**
9 | * 丢弃策略-抽象实现类
10 | * @author sivan
11 | */
12 | public abstract class AbstractSivanCacheEvict implements ISivanCacheEvict {
13 |
14 | @Override
15 | public ISivanCacheEntry evict(ISivanCacheEvictContext context) {
16 | //3. 返回结果
17 | return doEvict(context);
18 | }
19 |
20 | /**
21 | * 执行移除
22 | * @param context 上下文
23 | * @return 结果
24 | */
25 | protected abstract ISivanCacheEntry doEvict(ISivanCacheEvictContext context);
26 |
27 | @Override
28 | public void updateKey(K key) {
29 |
30 | }
31 |
32 | @Override
33 | public void removeKey(K key) {
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictClock.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.log.integration.core.Log;
5 | import com.github.houbb.log.integration.core.LogFactory;
6 | import com.xin.cache.api.ISivanCache;
7 | import com.xin.cache.api.ISivanCacheEntry;
8 | import com.xin.cache.api.ISivanCacheEvictContext;
9 | import com.xin.cache.model.SivanCacheEntry;
10 | import com.xin.cache.support.struct.lru.ILruMap;
11 | import com.xin.cache.support.struct.lru.impl.LruMapCircleList;
12 |
13 | /**
14 | * 淘汰策略-clock 算法
15 | *
16 | * @author sivan
17 | */
18 | public class SivanCacheEvictClock extends AbstractSivanCacheEvict {
19 |
20 | private static final Log log = LogFactory.getLog(SivanCacheEvictClock.class);
21 |
22 | /**
23 | * 循环链表
24 | *
25 | */
26 | private final ILruMap circleList;
27 |
28 | public SivanCacheEvictClock() {
29 | this.circleList = new LruMapCircleList<>();
30 | }
31 |
32 | @Override
33 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
34 | ISivanCacheEntry result = null;
35 | final ISivanCache cache = context.cache();
36 | // 超过限制,移除队尾的元素
37 | if(cache.size() >= context.size()) {
38 | ISivanCacheEntry evictEntry = circleList.removeEldest();;
39 | // 执行缓存移除操作
40 | final K evictKey = evictEntry.key();
41 | V evictValue = cache.remove(evictKey);
42 |
43 | log.debug("基于 clock 算法淘汰 key:{}, value: {}", evictKey, evictValue);
44 | result = new SivanCacheEntry<>(evictKey, evictValue);
45 | }
46 |
47 | return result;
48 | }
49 |
50 |
51 | /**
52 | * 更新信息
53 | * @param key 元素
54 | */
55 | @Override
56 | public void updateKey(final K key) {
57 | this.circleList.updateKey(key);
58 | }
59 |
60 | /**
61 | * 移除元素
62 | *
63 | * @param key 元素
64 | */
65 | @Override
66 | public void removeKey(final K key) {
67 | this.circleList.removeKey(key);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictContext.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.xin.cache.api.ISivanCache;
5 | import com.xin.cache.api.ISivanCacheEvictContext;
6 |
7 | /**
8 | * 驱除策略
9 | *
10 | * 1. 新加的 key
11 | * 2. map 实现
12 | * 3. 淘汰监听器
13 | *
14 | * @author sivan
15 | *
16 | * @param key
17 | * @param value
18 | */
19 | public class SivanCacheEvictContext implements ISivanCacheEvictContext {
20 |
21 | /**
22 | * 新加的 key
23 | *
24 | */
25 | private K key;
26 |
27 | /**
28 | * cache 实现
29 | *
30 | */
31 | private ISivanCache cache;
32 |
33 | /**
34 | * 最大的大小
35 | *
36 | */
37 | private int size;
38 |
39 | @Override
40 | public K key() {
41 | return key;
42 | }
43 |
44 | public SivanCacheEvictContext key(K key) {
45 | this.key = key;
46 | return this;
47 | }
48 |
49 | @Override
50 | public ISivanCache cache() {
51 | return cache;
52 | }
53 |
54 | public SivanCacheEvictContext cache(ISivanCache cache) {
55 | this.cache = cache;
56 | return this;
57 | }
58 |
59 | @Override
60 | public int size() {
61 | return size;
62 | }
63 |
64 | public SivanCacheEvictContext size(int size) {
65 | this.size = size;
66 | return this;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictFifo.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 | import com.xin.cache.api.ISivanCache;
4 | import com.xin.cache.api.ISivanCacheEvictContext;
5 | import com.xin.cache.model.SivanCacheEntry;
6 |
7 | import java.util.LinkedList;
8 | import java.util.Queue;
9 |
10 | /**
11 | * 丢弃策略-先进先出
12 | * @author sivan
13 | *
14 | */
15 | public class SivanCacheEvictFifo extends AbstractSivanCacheEvict {
16 |
17 | /**
18 | * queue 信息
19 | *
20 | */
21 | private final Queue queue = new LinkedList<>();
22 |
23 | @Override
24 | public SivanCacheEntry doEvict(ISivanCacheEvictContext context) {
25 | SivanCacheEntry result = null;
26 |
27 | final ISivanCache cache = context.cache();
28 | // 超过限制,执行移除
29 | if(cache.size() >= context.size()) {
30 | K evictKey = queue.remove();
31 | // 移除最开始的元素
32 | V evictValue = cache.remove(evictKey);
33 | result = new SivanCacheEntry<>(evictKey, evictValue);
34 | }
35 |
36 | // 将新加的元素放入队尾
37 | final K key = context.key();
38 | queue.add(key);
39 |
40 | return result;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLfu.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.heaven.util.lang.ObjectUtil;
5 | import com.github.houbb.heaven.util.util.CollectionUtil;
6 | import com.github.houbb.log.integration.core.Log;
7 | import com.github.houbb.log.integration.core.LogFactory;
8 | import com.xin.cache.api.ISivanCache;
9 | import com.xin.cache.api.ISivanCacheEntry;
10 | import com.xin.cache.api.ISivanCacheEvictContext;
11 | import com.xin.cache.exception.SivanCacheRuntimeException;
12 | import com.xin.cache.model.FreqNode;
13 | import com.xin.cache.model.SivanCacheEntry;
14 |
15 | import java.util.HashMap;
16 | import java.util.LinkedHashSet;
17 | import java.util.Map;
18 |
19 | /**
20 | * 丢弃策略-LFU 最少使用频次
21 | * @author sivan
22 | *
23 | */
24 | public class SivanCacheEvictLfu extends AbstractSivanCacheEvict {
25 |
26 | private static final Log log = LogFactory.getLog(SivanCacheEvictLfu.class);
27 |
28 | /**
29 | * key 映射信息
30 | *
31 | */
32 | private final Map> keyMap;
33 |
34 | /**
35 | * 频率 map
36 | *
37 | */
38 | private final Map>> freqMap;
39 |
40 | /**
41 | *
42 | * 最小频率
43 | *
44 | */
45 | private int minFreq;
46 |
47 | public SivanCacheEvictLfu() {
48 | this.keyMap = new HashMap<>();
49 | this.freqMap = new HashMap<>();
50 | this.minFreq = 1;
51 | }
52 |
53 | @Override
54 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
55 | ISivanCacheEntry result = null;
56 | final ISivanCache cache = context.cache();
57 | // 超过限制,移除频次最低的元素
58 | if(cache.size() >= context.size()) {
59 | FreqNode evictNode = this.getMinFreqNode();
60 | K evictKey = evictNode.key();
61 | V evictValue = cache.remove(evictKey);
62 |
63 | log.debug("淘汰最小频率信息, key: {}, value: {}, freq: {}",
64 | evictKey, evictValue, evictNode.frequency());
65 | result = new SivanCacheEntry<>(evictKey, evictValue);
66 | }
67 |
68 | return result;
69 | }
70 |
71 | /**
72 | * 获取最小频率的 节点
73 | *
74 | * @return 结果
75 | *
76 | */
77 | private FreqNode getMinFreqNode() {
78 | LinkedHashSet> set = freqMap.get(minFreq);
79 |
80 | if(CollectionUtil.isNotEmpty(set)) {
81 | return set.iterator().next();
82 | }
83 |
84 | throw new SivanCacheRuntimeException("未发现最小频率的 Key");
85 | }
86 |
87 |
88 | /**
89 | * 更新元素,更新 minFreq 信息
90 | * @param key 元素
91 | *
92 | */
93 | @Override
94 | public void updateKey(final K key) {
95 | FreqNode freqNode = keyMap.get(key);
96 |
97 | //1. 已经存在
98 | if(ObjectUtil.isNotNull(freqNode)) {
99 | //1.1 移除原始的节点信息
100 | int frequency = freqNode.frequency();
101 | LinkedHashSet> oldSet = freqMap.get(frequency);
102 | oldSet.remove(freqNode);
103 | //1.2 更新最小数据频率
104 | if (minFreq == frequency && oldSet.isEmpty()) {
105 | minFreq++;
106 | log.debug("minFreq 增加为:{}", minFreq);
107 | }
108 | //1.3 更新频率信息
109 | frequency++;
110 | freqNode.frequency(frequency);
111 | //1.4 放入新的集合
112 | this.addToFreqMap(frequency, freqNode);
113 | } else {
114 | //2. 不存在
115 | //2.1 构建新的元素
116 | FreqNode newNode = new FreqNode<>(key);
117 |
118 | //2.2 固定放入到频率为1的列表中
119 | this.addToFreqMap(1, newNode);
120 |
121 | //2.3 更新 minFreq 信息
122 | this.minFreq = 1;
123 |
124 | //2.4 添加到 keyMap
125 | this.keyMap.put(key, newNode);
126 | }
127 | }
128 |
129 | /**
130 | * 加入到频率 MAP
131 | * @param frequency 频率
132 | * @param freqNode 节点
133 | */
134 | private void addToFreqMap(final int frequency, FreqNode freqNode) {
135 | LinkedHashSet> set = freqMap.get(frequency);
136 | if (set == null) {
137 | set = new LinkedHashSet<>();
138 | }
139 | set.add(freqNode);
140 | freqMap.put(frequency, set);
141 | log.debug("freq={} 添加元素节点:{}", frequency, freqNode);
142 | }
143 |
144 | /**
145 | * 移除元素
146 | *
147 | * 1. 从 freqMap 中移除
148 | * 2. 从 keyMap 中移除
149 | * 3. 更新 minFreq 信息
150 | *
151 | * @param key 元素
152 | *
153 | */
154 | @Override
155 | public void removeKey(final K key) {
156 | FreqNode freqNode = this.keyMap.remove(key);
157 |
158 | //1. 根据 key 获取频率
159 | int freq = freqNode.frequency();
160 | LinkedHashSet> set = this.freqMap.get(freq);
161 |
162 | //2. 移除频率中对应的节点
163 | set.remove(freqNode);
164 | log.debug("freq={} 移除元素节点:{}", freq, freqNode);
165 |
166 | //3. 更新 minFreq
167 | if(CollectionUtil.isEmpty(set) && minFreq == freq) {
168 | minFreq--;
169 | log.debug("minFreq 降低为:{}", minFreq);
170 | }
171 | }
172 |
173 | }
174 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLru.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.log.integration.core.Log;
5 | import com.github.houbb.log.integration.core.LogFactory;
6 | import com.xin.cache.api.ISivanCache;
7 | import com.xin.cache.api.ISivanCacheEntry;
8 | import com.xin.cache.api.ISivanCacheEvictContext;
9 | import com.xin.cache.model.SivanCacheEntry;
10 |
11 | import java.util.LinkedList;
12 | import java.util.List;
13 |
14 | /**
15 | * 丢弃策略-LRU 最近最少使用
16 | * @author sivan
17 | *
18 | */
19 | public class SivanCacheEvictLru extends AbstractSivanCacheEvict {
20 |
21 | private static final Log log = LogFactory.getLog(SivanCacheEvictLru.class);
22 |
23 | /**
24 | * list 信息
25 | *
26 | */
27 | private final List list = new LinkedList<>();
28 |
29 | @Override
30 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
31 | ISivanCacheEntry result = null;
32 | final ISivanCache cache = context.cache();
33 | // 超过限制,移除队尾的元素
34 | if(cache.size() >= context.size()) {
35 | K evictKey = list.get(list.size()-1);
36 | V evictValue = cache.remove(evictKey);
37 | result = new SivanCacheEntry<>(evictKey, evictValue);
38 | }
39 |
40 | return result;
41 | }
42 |
43 |
44 | /**
45 | * 放入元素
46 | * (1)删除已经存在的
47 | * (2)新元素放到元素头部
48 | *
49 | * @param key 元素
50 | *
51 | */
52 | @Override
53 | public void updateKey(final K key) {
54 | this.list.remove(key);
55 | this.list.add(0, key);
56 | }
57 |
58 | /**
59 | * 移除元素
60 | * @param key 元素
61 | *
62 | */
63 | @Override
64 | public void removeKey(final K key) {
65 | this.list.remove(key);
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLru2.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.log.integration.core.Log;
5 | import com.github.houbb.log.integration.core.LogFactory;
6 | import com.xin.cache.api.ISivanCache;
7 | import com.xin.cache.api.ISivanCacheEntry;
8 | import com.xin.cache.api.ISivanCacheEvictContext;
9 | import com.xin.cache.model.SivanCacheEntry;
10 | import com.xin.cache.support.struct.lru.ILruMap;
11 | import com.xin.cache.support.struct.lru.impl.LruMapDoubleList;
12 |
13 | /**
14 | * 淘汰策略-LRU 最近最少使用
15 | *
16 | * 实现方式:Lru2
17 | * @author sivan
18 | *
19 | */
20 | public class SivanCacheEvictLru2 extends AbstractSivanCacheEvict {
21 |
22 | private static final Log log = LogFactory.getLog(SivanCacheEvictLru2.class);
23 |
24 | /**
25 | * 第一次访问的 lru
26 | *
27 | */
28 | private final ILruMap firstLruMap;
29 |
30 | /**
31 | * 2次及其以上的 lru
32 | *
33 | */
34 | private final ILruMap moreLruMap;
35 |
36 | public SivanCacheEvictLru2() {
37 | this.firstLruMap = new LruMapDoubleList<>();
38 | this.moreLruMap = new LruMapDoubleList<>();
39 | }
40 |
41 | @Override
42 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
43 | ISivanCacheEntry result = null;
44 | final ISivanCache cache = context.cache();
45 | // 超过限制,移除队尾的元素
46 | if(cache.size() >= context.size()) {
47 | ISivanCacheEntry evictEntry = null;
48 |
49 | //1. firstLruMap 不为空,优先移除队列中元素
50 | if(!firstLruMap.isEmpty()) {
51 | evictEntry = firstLruMap.removeEldest();
52 | log.debug("从 firstLruMap 中淘汰数据:{}", evictEntry);
53 | } else {
54 | //2. 否则从 moreLruMap 中淘汰数据
55 | evictEntry = moreLruMap.removeEldest();
56 | log.debug("从 moreLruMap 中淘汰数据:{}", evictEntry);
57 | }
58 |
59 | // 执行缓存移除操作
60 | final K evictKey = evictEntry.key();
61 | V evictValue = cache.remove(evictKey);
62 | result = new SivanCacheEntry<>(evictKey, evictValue);
63 | }
64 |
65 | return result;
66 | }
67 |
68 |
69 | /**
70 | * 更新信息
71 | * 1. 如果 moreLruMap 已经存在,则处理 more 队列,先删除,再插入。
72 | * 2. 如果 firstLruMap 中已经存在,则处理 first 队列,先删除 firstLruMap,然后插入 Lru。
73 | * 1 和 2 是不同的场景,但是代码实际上是一样的,删除逻辑中做了二种场景的兼容。
74 | *
75 | * 3. 如果不在1、2中,说明是新元素,直接插入到 firstLruMap 的开始即可。
76 | *
77 | * @param key 元素
78 | *
79 | */
80 | @Override
81 | public void updateKey(final K key) {
82 | //1. 元素已经在多次访问,或者第一次访问的 lru 中
83 | if(moreLruMap.contains(key)
84 | || firstLruMap.contains(key)) {
85 | //1.1 删除信息
86 | this.removeKey(key);
87 |
88 | //1.2 加入到多次 LRU 中
89 | moreLruMap.updateKey(key);
90 | log.debug("key: {} 多次访问,加入到 moreLruMap 中", key);
91 | } else {
92 | // 2. 加入到第一次访问 LRU 中
93 | firstLruMap.updateKey(key);
94 | log.debug("key: {} 为第一次访问,加入到 firstLruMap 中", key);
95 | }
96 | }
97 |
98 | /**
99 | * 移除元素
100 | *
101 | * 1. 多次 lru 中存在,删除
102 | * 2. 初次 lru 中存在,删除
103 | *
104 | * @param key 元素
105 | *
106 | */
107 | @Override
108 | public void removeKey(final K key) {
109 | //1. 多次LRU 删除逻辑
110 | if(moreLruMap.contains(key)) {
111 | moreLruMap.removeKey(key);
112 | log.debug("key: {} 从 moreLruMap 中移除", key);
113 | } else {
114 | firstLruMap.removeKey(key);
115 | log.debug("key: {} 从 firstLruMap 中移除", key);
116 | }
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLru2Q.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.heaven.util.lang.ObjectUtil;
5 | import com.github.houbb.log.integration.core.Log;
6 | import com.github.houbb.log.integration.core.LogFactory;
7 | import com.xin.cache.api.ISivanCache;
8 | import com.xin.cache.api.ISivanCacheEntry;
9 | import com.xin.cache.api.ISivanCacheEvictContext;
10 | import com.xin.cache.exception.SivanCacheRuntimeException;
11 | import com.xin.cache.model.DoubleListNode;
12 | import com.xin.cache.model.SivanCacheEntry;
13 |
14 | import java.util.HashMap;
15 | import java.util.LinkedList;
16 | import java.util.Map;
17 | import java.util.Queue;
18 |
19 | /**
20 | * 淘汰策略-LRU 最近最少使用
21 | *
22 | * 实现方式:Lru + FIFO
23 | * @author sivan
24 | *
25 | */
26 | public class SivanCacheEvictLru2Q extends AbstractSivanCacheEvict {
27 |
28 | private static final Log log = LogFactory.getLog(SivanCacheEvictLru2Q.class);
29 |
30 | /**
31 | * 队列大小限制
32 | *
33 | * 降低 O(n) 的消耗,避免耗时过长。
34 | *
35 | */
36 | private static final int LIMIT_QUEUE_SIZE = 1024;
37 |
38 | /**
39 | * 第一次访问的队列
40 | *
41 | */
42 | private Queue firstQueue;
43 |
44 | /**
45 | * 头结点
46 | *
47 | */
48 | private DoubleListNode head;
49 |
50 | /**
51 | * 尾巴结点
52 | *
53 | */
54 | private DoubleListNode tail;
55 |
56 | /**
57 | * map 信息
58 | *
59 | * key: 元素信息
60 | * value: 元素在 list 中对应的节点信息
61 | *
62 | */
63 | private Map> lruIndexMap;
64 |
65 | public SivanCacheEvictLru2Q() {
66 | this.firstQueue = new LinkedList<>();
67 | this.lruIndexMap = new HashMap<>();
68 | this.head = new DoubleListNode<>();
69 | this.tail = new DoubleListNode<>();
70 |
71 | this.head.next(this.tail);
72 | this.tail.pre(this.head);
73 | }
74 |
75 | @Override
76 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
77 | ISivanCacheEntry result = null;
78 | final ISivanCache cache = context.cache();
79 | // 超过限制,移除队尾的元素
80 | if(cache.size() >= context.size()) {
81 | K evictKey = null;
82 |
83 | //1. firstQueue 不为空,优先移除队列中元素
84 | if(!firstQueue.isEmpty()) {
85 | evictKey = firstQueue.remove();
86 | } else {
87 | // 获取尾巴节点的前一个元素
88 | DoubleListNode tailPre = this.tail.pre();
89 | if(tailPre == this.head) {
90 | log.error("当前列表为空,无法进行删除");
91 | throw new SivanCacheRuntimeException("不可删除头结点!");
92 | }
93 |
94 | evictKey = tailPre.key();
95 | }
96 |
97 | // 执行移除操作
98 | V evictValue = cache.remove(evictKey);
99 | result = new SivanCacheEntry<>(evictKey, evictValue);
100 | }
101 |
102 | return result;
103 | }
104 |
105 |
106 | /**
107 | * 放入元素
108 | * 1. 如果 lruIndexMap 已经存在,则处理 lru 队列,先删除,再插入。
109 | * 2. 如果 firstQueue 中已经存在,则处理 first 队列,先删除 firstQueue,然后插入 Lru。
110 | * 1 和 2 是不同的场景,但是代码实际上是一样的,删除逻辑中做了二种场景的兼容。
111 | *
112 | * 3. 如果不在1、2中,说明是新元素,直接插入到 firstQueue 的开始即可。
113 | *
114 | * @param key 元素
115 | *
116 | */
117 | @Override
118 | public void updateKey(final K key) {
119 | //1.1 是否在 LRU MAP 中
120 | //1.2 是否在 firstQueue 中
121 | DoubleListNode node = lruIndexMap.get(key);
122 | if(ObjectUtil.isNotNull(node)
123 | || firstQueue.contains(key)) {
124 | //1.3 删除信息
125 | this.removeKey(key);
126 |
127 | //1.4 加入到 LRU 中
128 | this.addToLruMapHead(key);
129 | return;
130 | }
131 |
132 | //2. 直接加入到 firstQueue 队尾
133 | // if(firstQueue.size() >= LIMIT_QUEUE_SIZE) {
134 | // // 避免第一次访问的列表一直增长,移除队头的元素
135 | // firstQueue.remove();
136 | // }
137 | firstQueue.add(key);
138 | }
139 |
140 | /**
141 | * 插入到 LRU Map 头部
142 | * @param key 元素
143 | *
144 | */
145 | private void addToLruMapHead(final K key) {
146 | //2. 新元素插入到头部
147 | //head<->next
148 | //变成:head<->new<->next
149 | DoubleListNode newNode = new DoubleListNode<>();
150 | newNode.key(key);
151 |
152 | DoubleListNode next = this.head.next();
153 | this.head.next(newNode);
154 | newNode.pre(this.head);
155 | next.pre(newNode);
156 | newNode.next(next);
157 |
158 | //2.2 插入到 map 中
159 | lruIndexMap.put(key, newNode);
160 | }
161 |
162 | /**
163 | * 移除元素
164 | *
165 | * 1. 获取 map 中的元素
166 | * 2. 不存在直接返回,存在执行以下步骤:
167 | * 2.1 删除双向链表中的元素
168 | * 2.2 删除 map 中的元素
169 | *
170 | * @param key 元素
171 | *
172 | */
173 | @Override
174 | public void removeKey(final K key) {
175 | DoubleListNode node = lruIndexMap.get(key);
176 |
177 | //1. LRU 删除逻辑
178 | if(ObjectUtil.isNotNull(node)) {
179 | // A<->B<->C
180 | // 删除 B,需要变成: A<->C
181 | DoubleListNode pre = node.pre();
182 | DoubleListNode next = node.next();
183 |
184 | pre.next(next);
185 | next.pre(pre);
186 |
187 | // 删除 map 中对应信息
188 | this.lruIndexMap.remove(node.key());
189 | } else {
190 | //2. FIFO 删除逻辑(O(n) 时间复杂度)
191 | firstQueue.remove(key);
192 | }
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLruDoubleListMap.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 |
4 | import com.github.houbb.heaven.util.lang.ObjectUtil;
5 | import com.github.houbb.log.integration.core.Log;
6 | import com.github.houbb.log.integration.core.LogFactory;
7 | import com.xin.cache.api.ISivanCache;
8 | import com.xin.cache.api.ISivanCacheEntry;
9 | import com.xin.cache.api.ISivanCacheEvictContext;
10 | import com.xin.cache.exception.SivanCacheRuntimeException;
11 | import com.xin.cache.model.DoubleListNode;
12 | import com.xin.cache.model.SivanCacheEntry;
13 |
14 | import java.util.HashMap;
15 | import java.util.Map;
16 |
17 | /**
18 | * 丢弃策略-LRU 最近最少使用
19 | *
20 | * 实现方式:HashMap + list 实现策略
21 | * @author sivan
22 | *
23 | */
24 | public class SivanCacheEvictLruDoubleListMap extends AbstractSivanCacheEvict {
25 |
26 | private static final Log log = LogFactory.getLog(SivanCacheEvictLruDoubleListMap.class);
27 |
28 |
29 | /**
30 | * 头结点
31 | *
32 | */
33 | private DoubleListNode head;
34 |
35 | /**
36 | * 尾巴结点
37 | *
38 | */
39 | private DoubleListNode tail;
40 |
41 | /**
42 | * map 信息
43 | *
44 | * key: 元素信息
45 | * value: 元素在 list 中对应的节点信息
46 | *
47 | */
48 | private Map> indexMap;
49 |
50 | public SivanCacheEvictLruDoubleListMap() {
51 | this.indexMap = new HashMap<>();
52 | this.head = new DoubleListNode<>();
53 | this.tail = new DoubleListNode<>();
54 |
55 | this.head.next(this.tail);
56 | this.tail.pre(this.head);
57 | }
58 |
59 | @Override
60 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) {
61 | ISivanCacheEntry result = null;
62 | final ISivanCache cache = context.cache();
63 | // 超过限制,移除队尾的元素
64 | if(cache.size() >= context.size()) {
65 | // 获取尾巴节点的前一个元素
66 | DoubleListNode tailPre = this.tail.pre();
67 | if(tailPre == this.head) {
68 | log.error("当前列表为空,无法进行删除");
69 | throw new SivanCacheRuntimeException("不可删除头结点!");
70 | }
71 |
72 | K evictKey = tailPre.key();
73 | V evictValue = cache.remove(evictKey);
74 | result = new SivanCacheEntry<>(evictKey, evictValue);
75 | }
76 |
77 | return result;
78 | }
79 |
80 |
81 | /**
82 | * 放入元素
83 | *
84 | * (1)删除已经存在的
85 | * (2)新元素放到元素头部
86 | *
87 | * @param key 元素
88 | *
89 | */
90 | @Override
91 | public void updateKey(final K key) {
92 | //1. 执行删除
93 | this.removeKey(key);
94 |
95 | //2. 新元素插入到头部
96 | //head<->next
97 | //变成:head<->new<->next
98 | DoubleListNode newNode = new DoubleListNode<>();
99 | newNode.key(key);
100 |
101 | DoubleListNode next = this.head.next();
102 | this.head.next(newNode);
103 | newNode.pre(this.head);
104 | next.pre(newNode);
105 | newNode.next(next);
106 |
107 | //2.2 插入到 map 中
108 | indexMap.put(key, newNode);
109 | }
110 |
111 | /**
112 | * 移除元素
113 | *
114 | * 1. 获取 map 中的元素
115 | * 2. 不存在直接返回,存在执行以下步骤:
116 | * 2.1 删除双向链表中的元素
117 | * 2.2 删除 map 中的元素
118 | *
119 | * @param key 元素
120 | *
121 | */
122 | @Override
123 | public void removeKey(final K key) {
124 | DoubleListNode node = indexMap.get(key);
125 |
126 | if(ObjectUtil.isNull(node)) {
127 | return;
128 | }
129 |
130 | // 删除 list node
131 | // A<->B<->C
132 | // 删除 B,需要变成: A<->C
133 | DoubleListNode pre = node.pre();
134 | DoubleListNode next = node.next();
135 |
136 | pre.next(next);
137 | next.pre(pre);
138 |
139 | // 删除 map 中对应信息
140 | this.indexMap.remove(key);
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictLruLinkedHashMap.java:
--------------------------------------------------------------------------------
1 | package com.xin.cache.support.evict;
2 |
3 | import com.github.houbb.log.integration.core.Log;
4 | import com.github.houbb.log.integration.core.LogFactory;
5 | import com.xin.cache.api.ISivanCache;
6 | import com.xin.cache.api.ISivanCacheEntry;
7 | import com.xin.cache.api.ISivanCacheEvict;
8 | import com.xin.cache.api.ISivanCacheEvictContext;
9 | import com.xin.cache.model.SivanCacheEntry;
10 |
11 | import java.util.LinkedHashMap;
12 | import java.util.Map;
13 |
14 | /**
15 | * 丢弃策略-LRU 最近最少使用
16 | *
17 | * 实现方式:LinkedHashMap
18 | * @author sivan
19 | *
20 | */
21 | public class SivanCacheEvictLruLinkedHashMap