├── .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 | ![SIVANCACHE Logo](https://p16-flow-sign-sg.ciciai.com/ocean-cloud-tos-sg/44b6453b8a67444ba9a4c2f258fa1df0.png~tplv-0es2k971ck-image.png?rk3s=18ea6f23&x-expires=1742024775&x-signature=6MIh%2FpRxBN0EjJsKr0UeS%2BH6tY0%3D) 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 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 extends LinkedHashMap 22 | implements ISivanCacheEvict { 23 | 24 | private static final Log log = LogFactory.getLog(SivanCacheEvictLruDoubleListMap.class); 25 | 26 | /** 27 | * 是否移除标识 28 | * 29 | */ 30 | private volatile boolean removeFlag = false; 31 | 32 | /** 33 | * 最旧的一个元素 34 | * 35 | */ 36 | private transient Map.Entry eldest = null; 37 | 38 | public SivanCacheEvictLruLinkedHashMap() { 39 | super(16, 0.75f, true); 40 | } 41 | 42 | @Override 43 | public ISivanCacheEntry evict(ISivanCacheEvictContext context) { 44 | ISivanCacheEntry result = null; 45 | final ISivanCache cache = context.cache(); 46 | // 超过限制,移除队尾的元素 47 | if(cache.size() >= context.size()) { 48 | removeFlag = true; 49 | 50 | // 执行 put 操作 51 | super.put(context.key(), null); 52 | 53 | // 构建淘汰的元素 54 | K evictKey = eldest.getKey(); 55 | V evictValue = cache.remove(evictKey); 56 | result = new SivanCacheEntry<>(evictKey, evictValue); 57 | } else { 58 | removeFlag = false; 59 | } 60 | 61 | return result; 62 | } 63 | 64 | @Override 65 | protected boolean removeEldestEntry(Map.Entry eldest) { 66 | this.eldest = eldest; 67 | return removeFlag; 68 | } 69 | 70 | @Override 71 | public void updateKey(K key) { 72 | super.put(key, null); 73 | } 74 | 75 | @Override 76 | public void removeKey(K key) { 77 | super.remove(key); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvictNone.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.evict; 2 | 3 | import com.xin.cache.api.ISivanCacheEntry; 4 | import com.xin.cache.api.ISivanCacheEvictContext; 5 | 6 | /** 7 | * 丢弃策略 8 | * @author sivan 9 | * 10 | */ 11 | public class SivanCacheEvictNone extends AbstractSivanCacheEvict { 12 | 13 | @Override 14 | protected ISivanCacheEntry doEvict(ISivanCacheEvictContext context) { 15 | return null; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/evict/SivanCacheEvicts.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.evict; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheEvict; 5 | 6 | /** 7 | * 丢弃策略 8 | * 9 | * @author sivan 10 | * 11 | */ 12 | public final class SivanCacheEvicts { 13 | 14 | private SivanCacheEvicts(){} 15 | 16 | /** 17 | * 无策略 18 | * 19 | * @param key 20 | * @param value 21 | * @return 结果 22 | * 23 | */ 24 | public static ISivanCacheEvict none() { 25 | return new SivanCacheEvictNone<>(); 26 | } 27 | 28 | /** 29 | * 先进先出 30 | * 31 | * @param key 32 | * @param value 33 | * @return 结果 34 | * 35 | */ 36 | public static ISivanCacheEvict fifo() { 37 | return new SivanCacheEvictFifo<>(); 38 | } 39 | 40 | /** 41 | * LRU 驱除策略 42 | * 43 | * @param key 44 | * @param value 45 | * @return 结果 46 | * 47 | */ 48 | public static ISivanCacheEvict lru() { 49 | return new SivanCacheEvictLru<>(); 50 | } 51 | 52 | /** 53 | * LRU 驱除策略 54 | * 55 | * 基于双向链表 + map 实现 56 | * @param key 57 | * @param value 58 | * @return 结果 59 | * 60 | */ 61 | public static ISivanCacheEvict lruDoubleListMap() { 62 | return new SivanCacheEvictLruDoubleListMap<>(); 63 | } 64 | 65 | 66 | /** 67 | * LRU 驱除策略 68 | * 69 | * 基于LinkedHashMap 70 | * @param key 71 | * @param value 72 | * @return 结果 73 | * 74 | */ 75 | public static ISivanCacheEvict lruLinkedHashMap() { 76 | return new SivanCacheEvictLruLinkedHashMap<>(); 77 | } 78 | 79 | /** 80 | * LRU 驱除策略 81 | * 82 | * 基于 2Q 实现 83 | * @param key 84 | * @param value 85 | * @return 结果 86 | * 87 | */ 88 | public static ISivanCacheEvict lru2Q() { 89 | return new SivanCacheEvictLru2Q<>(); 90 | } 91 | 92 | /** 93 | * LRU 驱除策略 94 | * 95 | * 基于 LRU-2 实现 96 | * @param key 97 | * @param value 98 | * @return 结果 99 | * 100 | */ 101 | public static ISivanCacheEvict lru2() { 102 | return new SivanCacheEvictLru2<>(); 103 | } 104 | 105 | /** 106 | * LFU 驱除策略 107 | * 108 | * 基于 LFU 实现 109 | * @param key 110 | * @param value 111 | * @return 结果 112 | */ 113 | public static ISivanCacheEvict lfu() { 114 | return new SivanCacheEvictLfu<>(); 115 | } 116 | 117 | /** 118 | * 时钟算法 119 | * @param key 120 | * @param value 121 | * @return 结果 122 | */ 123 | public static ISivanCacheEvict clock() { 124 | return new SivanCacheEvictClock<>(); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/expire/SivanCacheExpire.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.expire; 2 | 3 | import com.github.houbb.heaven.util.util.CollectionUtil; 4 | import com.github.houbb.heaven.util.util.MapUtil; 5 | import com.xin.cache.api.ISivanCache; 6 | import com.xin.cache.api.ISivanCacheExpire; 7 | import com.xin.cache.api.ISivanCacheRemoveListener; 8 | import com.xin.cache.api.ISivanCacheRemoveListenerContext; 9 | import com.xin.cache.constant.SivanCacheRemoveType; 10 | import com.xin.cache.support.listener.remove.SivanCacheRemoveListenerContext; 11 | 12 | import java.util.Collection; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.concurrent.Executors; 16 | import java.util.concurrent.ScheduledExecutorService; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * 缓存过期-普通策略 21 | * 22 | * @author sivan 23 | * @param key 24 | * @param value 25 | */ 26 | public class SivanCacheExpire implements ISivanCacheExpire { 27 | 28 | /** 29 | * 单次清空的数量限制 30 | * 31 | */ 32 | private static final int LIMIT = 100; 33 | 34 | /** 35 | * 过期 map 36 | * 37 | * 空间换时间 38 | * 39 | */ 40 | private final Map expireMap = new HashMap<>(); 41 | 42 | /** 43 | * 缓存实现 44 | * 45 | */ 46 | private final ISivanCache cache; 47 | 48 | /** 49 | * 线程执行类 50 | * 51 | */ 52 | private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 53 | 54 | public SivanCacheExpire(ISivanCache cache) { 55 | this.cache = cache; 56 | this.init(); 57 | } 58 | 59 | /** 60 | * 初始化任务 61 | * 62 | */ 63 | private void init() { 64 | EXECUTOR_SERVICE.scheduleAtFixedRate(new ExpireThread(), 100, 100, TimeUnit.MILLISECONDS); 65 | } 66 | 67 | /** 68 | * 定时执行任务 69 | * 70 | */ 71 | private class ExpireThread implements Runnable { 72 | @Override 73 | public void run() { 74 | //1.判断是否为空 75 | if(MapUtil.isEmpty(expireMap)) { 76 | return; 77 | } 78 | 79 | //2. 获取 key 进行处理 80 | int count = 0; 81 | for(Map.Entry entry : expireMap.entrySet()) { 82 | if(count >= LIMIT) { 83 | return; 84 | } 85 | 86 | expireKey(entry.getKey(), entry.getValue()); 87 | count++; 88 | } 89 | } 90 | } 91 | 92 | @Override 93 | public void expire(K key, long expireAt) { 94 | expireMap.put(key, expireAt); 95 | } 96 | 97 | @Override 98 | public void refreshExpire(Collection keyList) { 99 | if(CollectionUtil.isEmpty(keyList)) { 100 | return; 101 | } 102 | 103 | // 判断大小,小的作为外循环。一般都是过期的 keys 比较小。 104 | if(keyList.size() <= expireMap.size()) { 105 | for(K key : keyList) { 106 | Long expireAt = expireMap.get(key); 107 | expireKey(key, expireAt); 108 | } 109 | } else { 110 | for(Map.Entry entry : expireMap.entrySet()) { 111 | this.expireKey(entry.getKey(), entry.getValue()); 112 | } 113 | } 114 | } 115 | 116 | @Override 117 | public Long expireTime(K key) { 118 | return expireMap.get(key); 119 | } 120 | 121 | /** 122 | * 过期处理 key 123 | * @param key key 124 | * @param expireAt 过期时间 125 | * 126 | */ 127 | private void expireKey(final K key, final Long expireAt) { 128 | if(expireAt == null) { 129 | return; 130 | } 131 | 132 | long currentTime = System.currentTimeMillis(); 133 | if(currentTime >= expireAt) { 134 | expireMap.remove(key); 135 | // 再移除缓存,后续可以通过惰性删除做补偿 136 | V removeValue = cache.remove(key); 137 | 138 | // 执行淘汰监听器 139 | ISivanCacheRemoveListenerContext removeListenerContext = SivanCacheRemoveListenerContext.newInstance().key(key).value(removeValue).type(SivanCacheRemoveType.EXPIRE.code()); 140 | for(ISivanCacheRemoveListener listener : cache.removeListeners()) { 141 | listener.listen(removeListenerContext); 142 | } 143 | } 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/expire/SivanCacheExpireRandom.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.expire; 2 | 3 | import com.github.houbb.heaven.util.util.CollectionUtil; 4 | import com.github.houbb.heaven.util.util.MapUtil; 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.ISivanCacheExpire; 9 | import com.xin.cache.api.ISivanCacheRemoveListener; 10 | import com.xin.cache.api.ISivanCacheRemoveListenerContext; 11 | import com.xin.cache.constant.SivanCacheRemoveType; 12 | import com.xin.cache.exception.SivanCacheRuntimeException; 13 | import com.xin.cache.support.listener.remove.SivanCacheRemoveListenerContext; 14 | 15 | import java.util.*; 16 | import java.util.concurrent.Executors; 17 | import java.util.concurrent.ScheduledExecutorService; 18 | import java.util.concurrent.ThreadLocalRandom; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * 缓存过期-普通策略随机 23 | * 24 | * @author sivan 25 | * 26 | * @param key 27 | * @param value 28 | */ 29 | public class SivanCacheExpireRandom implements ISivanCacheExpire { 30 | 31 | private static final Log log = LogFactory.getLog(SivanCacheExpireRandom.class); 32 | 33 | /** 34 | * 单次清空的数量限制 35 | * 36 | */ 37 | private static final int COUNT_LIMIT = 100; 38 | 39 | /** 40 | * 过期 map 41 | * 42 | * 空间换时间 43 | * 44 | */ 45 | private final Map expireMap = new HashMap<>(); 46 | 47 | /** 48 | * 缓存实现 49 | * 50 | */ 51 | private final ISivanCache cache; 52 | 53 | /** 54 | * 是否启用快模式 55 | * 56 | */ 57 | private volatile boolean fastMode = false; 58 | 59 | /** 60 | * 线程执行类 61 | * 62 | */ 63 | private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 64 | 65 | public SivanCacheExpireRandom(ISivanCache cache) { 66 | this.cache = cache; 67 | this.init(); 68 | } 69 | 70 | /** 71 | * 初始化任务 72 | * 73 | */ 74 | private void init() { 75 | EXECUTOR_SERVICE.scheduleAtFixedRate(new ExpireThreadRandom(), 10, 10, TimeUnit.SECONDS); 76 | } 77 | 78 | /** 79 | * 定时执行任务 80 | * 81 | */ 82 | private class ExpireThreadRandom implements Runnable { 83 | @Override 84 | public void run() { 85 | //1.判断是否为空 86 | if(MapUtil.isEmpty(expireMap)) { 87 | log.info("expireMap 信息为空,直接跳过本次处理。"); 88 | return; 89 | } 90 | 91 | //2. 是否启用快模式 92 | if(fastMode) { 93 | expireKeys(10L); 94 | } 95 | 96 | //3. 缓慢模式 97 | expireKeys(100L); 98 | } 99 | } 100 | 101 | 102 | /** 103 | * 过期信息 104 | * @param timeoutMills 超时时间 105 | * 106 | */ 107 | private void expireKeys(final long timeoutMills) { 108 | // 设置超时时间 100ms 109 | final long timeLimit = System.currentTimeMillis() + timeoutMills; 110 | // 恢复 fastMode 111 | this.fastMode = false; 112 | 113 | //2. 获取 key 进行处理 114 | int count = 0; 115 | while (true) { 116 | //2.1 返回判断 117 | if(count >= COUNT_LIMIT) { 118 | log.info("过期淘汰次数已经达到最大次数: {},完成本次执行。", COUNT_LIMIT); 119 | return; 120 | } 121 | if(System.currentTimeMillis() >= timeLimit) { 122 | this.fastMode = true; 123 | log.info("过期淘汰已经达到限制时间,中断本次执行,设置 fastMode=true;"); 124 | return; 125 | } 126 | 127 | //2.2 随机过期 128 | K key = getRandomKey(); 129 | Long expireAt = expireMap.get(key); 130 | boolean expireFlag = expireKey(key, expireAt); 131 | log.debug("key: {} 过期执行结果 {}", key, expireFlag); 132 | 133 | //2.3 信息更新 134 | count++; 135 | } 136 | } 137 | 138 | 139 | /** 140 | * 随机获取一个 key 信息 141 | * @return 随机返回的 keys 142 | * 143 | */ 144 | private K getRandomKey() { 145 | Random random = ThreadLocalRandom.current(); 146 | 147 | Set keySet = expireMap.keySet(); 148 | List list = new ArrayList<>(keySet); 149 | int randomIndex = random.nextInt(list.size()); 150 | return list.get(randomIndex); 151 | } 152 | 153 | /** 154 | * 随机获取一个 key 信息 155 | * @return 随机返回的 keys 156 | * 157 | */ 158 | private K getRandomKey2() { 159 | Random random = ThreadLocalRandom.current(); 160 | int randomIndex = random.nextInt(expireMap.size()); 161 | 162 | // 遍历 keys 163 | Iterator iterator = expireMap.keySet().iterator(); 164 | int count = 0; 165 | while (iterator.hasNext()) { 166 | K key = iterator.next(); 167 | 168 | if(count == randomIndex) { 169 | return key; 170 | } 171 | count++; 172 | } 173 | 174 | // 正常逻辑不会到这里 175 | throw new SivanCacheRuntimeException("对应信息不存在"); 176 | } 177 | 178 | /** 179 | * 批量获取多个 key 信息 180 | * @param sizeLimit 大小限制 181 | * @return 随机返回的 keys 182 | * 183 | */ 184 | private Set getRandomKeyBatch(final int sizeLimit) { 185 | Random random = ThreadLocalRandom.current(); 186 | int randomIndex = random.nextInt(expireMap.size()); 187 | 188 | // 遍历 keys 189 | Iterator iterator = expireMap.keySet().iterator(); 190 | int count = 0; 191 | 192 | Set keySet = new HashSet<>(); 193 | while (iterator.hasNext()) { 194 | // 判断列表大小 195 | if(keySet.size() >= sizeLimit) { 196 | return keySet; 197 | } 198 | 199 | K key = iterator.next(); 200 | // index 向后的位置,全部放进来。 201 | if(count >= randomIndex) { 202 | keySet.add(key); 203 | } 204 | count++; 205 | } 206 | 207 | // 正常逻辑不会到这里 208 | throw new SivanCacheRuntimeException("对应信息不存在"); 209 | } 210 | 211 | @Override 212 | public void expire(K key, long expireAt) { 213 | expireMap.put(key, expireAt); 214 | } 215 | 216 | @Override 217 | public void refreshExpire(Collection keyList) { 218 | if(CollectionUtil.isEmpty(keyList)) { 219 | return; 220 | } 221 | 222 | // 判断大小,小的作为外循环。一般都是过期的 keys 比较小。 223 | if(keyList.size() <= expireMap.size()) { 224 | for(K key : keyList) { 225 | Long expireAt = expireMap.get(key); 226 | expireKey(key, expireAt); 227 | } 228 | } else { 229 | for(Map.Entry entry : expireMap.entrySet()) { 230 | this.expireKey(entry.getKey(), entry.getValue()); 231 | } 232 | } 233 | } 234 | 235 | @Override 236 | public Long expireTime(K key) { 237 | return expireMap.get(key); 238 | } 239 | 240 | /** 241 | * 过期处理 key 242 | * @param key key 243 | * @param expireAt 过期时间 244 | * 245 | * @return 是否执行过期 246 | */ 247 | private boolean expireKey(final K key, final Long expireAt) { 248 | if(expireAt == null) { 249 | return false; 250 | } 251 | 252 | long currentTime = System.currentTimeMillis(); 253 | if(currentTime >= expireAt) { 254 | expireMap.remove(key); 255 | // 再移除缓存,后续可以通过惰性删除做补偿 256 | V removeValue = cache.remove(key); 257 | 258 | // 执行淘汰监听器 259 | ISivanCacheRemoveListenerContext removeListenerContext = SivanCacheRemoveListenerContext.newInstance().key(key).value(removeValue).type(SivanCacheRemoveType.EXPIRE.code()); 260 | for(ISivanCacheRemoveListener listener : cache.removeListeners()) { 261 | listener.listen(removeListenerContext); 262 | } 263 | 264 | return true; 265 | } 266 | 267 | return false; 268 | } 269 | 270 | } 271 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/expire/SivanCacheExpireSort.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.expire; 2 | 3 | import com.github.houbb.heaven.util.util.CollectionUtil; 4 | import com.github.houbb.heaven.util.util.MapUtil; 5 | import com.xin.cache.api.ISivanCache; 6 | import com.xin.cache.api.ISivanCacheExpire; 7 | 8 | import java.util.*; 9 | import java.util.concurrent.Executors; 10 | import java.util.concurrent.ScheduledExecutorService; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * 缓存过期-时间排序策略 15 | * 16 | * 优点:定时删除时不用做过多消耗 17 | * 缺点:惰性删除不友好 18 | * 19 | * @author sivan 20 | * @param key 21 | * @param value 22 | */ 23 | public class SivanCacheExpireSort implements ISivanCacheExpire { 24 | 25 | /** 26 | * 单次清空的数量限制 27 | * 28 | */ 29 | private static final int LIMIT = 100; 30 | 31 | /** 32 | * 排序缓存存储 33 | * 34 | * 使用按照时间排序的缓存处理。 35 | * 36 | */ 37 | private final Map> sortMap = new TreeMap<>(new Comparator() { 38 | @Override 39 | public int compare(Long o1, Long o2) { 40 | return (int) (o1-o2); 41 | } 42 | }); 43 | 44 | /** 45 | * 过期 map 46 | * 47 | * 空间换时间 48 | * 49 | */ 50 | private final Map expireMap = new HashMap<>(); 51 | 52 | /** 53 | * 缓存实现 54 | * 55 | */ 56 | private final ISivanCache cache; 57 | 58 | /** 59 | * 线程执行类 60 | * 61 | */ 62 | private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 63 | 64 | public SivanCacheExpireSort(ISivanCache cache) { 65 | this.cache = cache; 66 | this.init(); 67 | } 68 | 69 | /** 70 | * 初始化任务 71 | * 72 | */ 73 | private void init() { 74 | EXECUTOR_SERVICE.scheduleAtFixedRate(new ExpireThread(), 100, 100, TimeUnit.MILLISECONDS); 75 | } 76 | 77 | /** 78 | * 定时执行任务 79 | * 80 | */ 81 | private class ExpireThread implements Runnable { 82 | @Override 83 | public void run() { 84 | //1.判断是否为空 85 | if(MapUtil.isEmpty(sortMap)) { 86 | return; 87 | } 88 | 89 | //2. 获取 key 进行处理 90 | int count = 0; 91 | for(Map.Entry> entry : sortMap.entrySet()) { 92 | final Long expireAt = entry.getKey(); 93 | List expireKeys = entry.getValue(); 94 | 95 | // 判断队列是否为空 96 | if(CollectionUtil.isEmpty(expireKeys)) { 97 | sortMap.remove(expireAt); 98 | continue; 99 | } 100 | if(count >= LIMIT) { 101 | return; 102 | } 103 | 104 | // 删除的逻辑处理 105 | long currentTime = System.currentTimeMillis(); 106 | if(currentTime >= expireAt) { 107 | Iterator iterator = expireKeys.iterator(); 108 | while (iterator.hasNext()) { 109 | K key = iterator.next(); 110 | // 先移除本身 111 | iterator.remove(); 112 | expireMap.remove(key); 113 | 114 | // 再移除缓存,后续可以通过惰性删除做补偿 115 | cache.remove(key); 116 | 117 | count++; 118 | } 119 | } else { 120 | // 直接跳过,没有过期的信息 121 | return; 122 | } 123 | } 124 | } 125 | } 126 | 127 | @Override 128 | public void expire(K key, long expireAt) { 129 | List keys = sortMap.get(expireAt); 130 | if(keys == null) { 131 | keys = new ArrayList<>(); 132 | } 133 | keys.add(key); 134 | 135 | // 设置对应的信息 136 | sortMap.put(expireAt, keys); 137 | expireMap.put(key, expireAt); 138 | } 139 | 140 | @Override 141 | public void refreshExpire(Collection keyList) { 142 | if(CollectionUtil.isEmpty(keyList)) { 143 | return; 144 | } 145 | 146 | // 这样维护两套的代价太大,后续优化,暂时不用。 147 | // 判断大小,小的作为外循环 148 | final int expireSize = expireMap.size(); 149 | if(expireSize <= keyList.size()) { 150 | // 一般过期的数量都是较少的 151 | for(Map.Entry entry : expireMap.entrySet()) { 152 | K key = entry.getKey(); 153 | 154 | // 这里直接执行过期处理,不再判断是否存在于集合中。 155 | // 因为基于集合的判断,时间复杂度为 O(n) 156 | this.removeExpireKey(key); 157 | } 158 | } else { 159 | for(K key : keyList) { 160 | this.removeExpireKey(key); 161 | } 162 | } 163 | } 164 | 165 | /** 166 | * 移除过期信息 167 | * @param key key 168 | * 169 | */ 170 | private void removeExpireKey(final K key) { 171 | Long expireTime = expireMap.get(key); 172 | if(expireTime != null) { 173 | final long currentTime = System.currentTimeMillis(); 174 | if(currentTime >= expireTime) { 175 | expireMap.remove(key); 176 | 177 | List expireKeys = sortMap.get(expireTime); 178 | expireKeys.remove(key); 179 | sortMap.put(expireTime, expireKeys); 180 | } 181 | } 182 | } 183 | 184 | @Override 185 | public Long expireTime(K key) { 186 | return expireMap.get(key); 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/SivanCacheInterceptorContext.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor; 2 | 3 | 4 | import com.xin.cache.api.ISivanCache; 5 | import com.xin.cache.api.ISivanCacheInterceptorContext; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * 耗时统计 11 | * 12 | * (1)耗时 13 | * (2)慢日志 14 | * @author sivan 15 | * 16 | * @param key 17 | * @param value 18 | */ 19 | public class SivanCacheInterceptorContext implements ISivanCacheInterceptorContext { 20 | 21 | private ISivanCache cache; 22 | 23 | /** 24 | * 执行的方法信息 25 | * 26 | */ 27 | private Method method; 28 | 29 | /** 30 | * 执行的参数 31 | * 32 | */ 33 | private Object[] params; 34 | 35 | /** 36 | * 方法执行的结果 37 | * 38 | */ 39 | private Object result; 40 | 41 | /** 42 | * 开始时间 43 | * 44 | */ 45 | private long startMills; 46 | 47 | /** 48 | * 结束时间 49 | * 50 | */ 51 | private long endMills; 52 | 53 | public static SivanCacheInterceptorContext newInstance() { 54 | return new SivanCacheInterceptorContext<>(); 55 | } 56 | 57 | @Override 58 | public ISivanCache cache() { 59 | return cache; 60 | } 61 | 62 | public SivanCacheInterceptorContext cache(ISivanCache cache) { 63 | this.cache = cache; 64 | return this; 65 | } 66 | 67 | @Override 68 | public Method method() { 69 | return method; 70 | } 71 | 72 | public SivanCacheInterceptorContext method(Method method) { 73 | this.method = method; 74 | return this; 75 | } 76 | 77 | @Override 78 | public Object[] params() { 79 | return params; 80 | } 81 | 82 | public SivanCacheInterceptorContext params(Object[] params) { 83 | this.params = params; 84 | return this; 85 | } 86 | 87 | @Override 88 | public Object result() { 89 | return result; 90 | } 91 | 92 | public SivanCacheInterceptorContext result(Object result) { 93 | this.result = result; 94 | return this; 95 | } 96 | 97 | @Override 98 | public long startMills() { 99 | return startMills; 100 | } 101 | 102 | public SivanCacheInterceptorContext startMills(long startMills) { 103 | this.startMills = startMills; 104 | return this; 105 | } 106 | 107 | @Override 108 | public long endMills() { 109 | return endMills; 110 | } 111 | 112 | public SivanCacheInterceptorContext endMills(long endMills) { 113 | this.endMills = endMills; 114 | return this; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/SivanCacheInterceptors.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheInterceptor; 5 | import com.xin.cache.support.interceptor.aof.SivanCacheInterceptorAof; 6 | import com.xin.cache.support.interceptor.common.SivanCacheInterceptorCost; 7 | import com.xin.cache.support.interceptor.evict.SivanCacheInterceptorEvict; 8 | import com.xin.cache.support.interceptor.refresh.SivanCacheInterceptorRefresh; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * 缓存拦截器工具类 15 | * @author sivan 16 | * 17 | */ 18 | public final class SivanCacheInterceptors { 19 | 20 | /** 21 | * 默认通用 22 | * @return 结果 23 | * 24 | */ 25 | @SuppressWarnings("all") 26 | public static List defaultCommonList() { 27 | List list = new ArrayList<>(); 28 | list.add(new SivanCacheInterceptorCost()); 29 | return list; 30 | } 31 | 32 | /** 33 | * 默认刷新 34 | * @return 结果 35 | * 36 | */ 37 | @SuppressWarnings("all") 38 | public static List defaultRefreshList() { 39 | List list = new ArrayList<>(); 40 | list.add(new SivanCacheInterceptorRefresh()); 41 | return list; 42 | } 43 | 44 | /** 45 | * AOF 模式 46 | * @return 结果 47 | * 48 | */ 49 | @SuppressWarnings("all") 50 | public static ISivanCacheInterceptor aof() { 51 | return new SivanCacheInterceptorAof(); 52 | } 53 | 54 | /** 55 | * 驱除策略拦截器 56 | * @return 结果 57 | * 58 | */ 59 | @SuppressWarnings("all") 60 | public static ISivanCacheInterceptor evict() { 61 | return new SivanCacheInterceptorEvict(); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/aof/SivanCacheInterceptorAof.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor.aof; 2 | 3 | import com.alibaba.fastjson.JSON; 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.ISivanCacheInterceptor; 8 | import com.xin.cache.api.ISivanCacheInterceptorContext; 9 | import com.xin.cache.api.ISivanCachePersist; 10 | import com.xin.cache.model.PersistAofEntry; 11 | import com.xin.cache.support.persist.SivanCachePersistAof; 12 | 13 | /** 14 | * 顺序追加模式 15 | * 16 | * AOF 持久化到文件,暂时不考虑 buffer 等特性。 17 | * @author sivan 18 | * 19 | */ 20 | public class SivanCacheInterceptorAof implements ISivanCacheInterceptor { 21 | 22 | private static final Log log = LogFactory.getLog(SivanCacheInterceptorAof.class); 23 | 24 | @Override 25 | public void before(ISivanCacheInterceptorContext context) { 26 | } 27 | 28 | @Override 29 | public void after(ISivanCacheInterceptorContext context) { 30 | // 持久化类 31 | ISivanCache cache = context.cache(); 32 | ISivanCachePersist persist = cache.persist(); 33 | 34 | if(persist instanceof SivanCachePersistAof) { 35 | SivanCachePersistAof cachePersistAof = (SivanCachePersistAof) persist; 36 | 37 | String methodName = context.method().getName(); 38 | PersistAofEntry aofEntry = PersistAofEntry.newInstance(); 39 | aofEntry.setMethodName(methodName); 40 | aofEntry.setParams(context.params()); 41 | 42 | String json = JSON.toJSONString(aofEntry); 43 | 44 | // 直接持久化 45 | log.debug("AOF 开始追加文件内容:{}", json); 46 | cachePersistAof.append(json); 47 | log.debug("AOF 完成追加文件内容:{}", json); 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/common/SivanCacheInterceptorCost.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor.common; 2 | 3 | import com.github.houbb.heaven.util.util.CollectionUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.xin.cache.api.ISivanCacheInterceptor; 7 | import com.xin.cache.api.ISivanCacheInterceptorContext; 8 | import com.xin.cache.api.ISivanCacheSlowListener; 9 | import com.xin.cache.support.listener.slow.SivanCacheSlowListenerContext; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * 耗时统计 15 | * 16 | * (1)耗时 17 | * (2)慢日志 18 | * @author sivan 19 | * 20 | * @param key 21 | * @param value 22 | */ 23 | public class SivanCacheInterceptorCost implements ISivanCacheInterceptor { 24 | 25 | private static final Log log = LogFactory.getLog(SivanCacheInterceptorCost.class); 26 | 27 | @Override 28 | public void before(ISivanCacheInterceptorContext context) { 29 | log.debug("Cost start, method: {}", context.method().getName()); 30 | } 31 | 32 | @Override 33 | public void after(ISivanCacheInterceptorContext context) { 34 | long costMills = context.endMills()-context.startMills(); 35 | final String methodName = context.method().getName(); 36 | log.debug("Cost end, method: {}, cost: {}ms", methodName, costMills); 37 | 38 | // 添加慢日志操作 39 | List slowListeners = context.cache().slowListeners(); 40 | if(CollectionUtil.isNotEmpty(slowListeners)) { 41 | SivanCacheSlowListenerContext listenerContext = SivanCacheSlowListenerContext.newInstance().startTimeMills(context.startMills()) 42 | .endTimeMills(context.endMills()) 43 | .costTimeMills(costMills) 44 | .methodName(methodName) 45 | .params(context.params()) 46 | .result(context.result()) 47 | ; 48 | 49 | // 设置多个,可以考虑不同的慢日志级别,做不同的处理 50 | for(ISivanCacheSlowListener slowListener : slowListeners) { 51 | long slowThanMills = slowListener.slowerThanMills(); 52 | if(costMills >= slowThanMills) { 53 | slowListener.listen(listenerContext); 54 | } 55 | } 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/evict/SivanCacheInterceptorEvict.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor.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.ISivanCacheEvict; 6 | import com.xin.cache.api.ISivanCacheInterceptor; 7 | import com.xin.cache.api.ISivanCacheInterceptorContext; 8 | 9 | import java.lang.reflect.Method; 10 | 11 | /** 12 | * 驱除策略拦截器 13 | * 14 | * @author sivan 15 | * 16 | */ 17 | public class SivanCacheInterceptorEvict implements ISivanCacheInterceptor { 18 | 19 | private static final Log log = LogFactory.getLog(SivanCacheInterceptorEvict.class); 20 | 21 | @Override 22 | public void before(ISivanCacheInterceptorContext context) { 23 | } 24 | 25 | @Override 26 | @SuppressWarnings("all") 27 | public void after(ISivanCacheInterceptorContext context) { 28 | ISivanCacheEvict evict = context.cache().evict(); 29 | 30 | Method method = context.method(); 31 | final K key = (K) context.params()[0]; 32 | if("remove".equals(method.getName())) { 33 | evict.removeKey(key); 34 | } else { 35 | evict.updateKey(key); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/interceptor/refresh/SivanCacheInterceptorRefresh.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.interceptor.refresh; 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.ISivanCacheInterceptor; 7 | import com.xin.cache.api.ISivanCacheInterceptorContext; 8 | 9 | /** 10 | * 刷新 11 | * 12 | * @author sivan 13 | * 14 | */ 15 | public class SivanCacheInterceptorRefresh implements ISivanCacheInterceptor { 16 | 17 | private static final Log log = LogFactory.getLog(SivanCacheInterceptorRefresh.class); 18 | 19 | @Override 20 | public void before(ISivanCacheInterceptorContext context) { 21 | log.debug("Refresh start"); 22 | final ISivanCache cache = context.cache(); 23 | cache.expire().refreshExpire(cache.keySet()); 24 | } 25 | 26 | @Override 27 | public void after(ISivanCacheInterceptorContext context) { 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/remove/SivanCacheRemoveListener.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.remove; 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.ISivanCacheRemoveListener; 6 | import com.xin.cache.api.ISivanCacheRemoveListenerContext; 7 | 8 | /** 9 | * 默认的删除监听类 10 | * @author sivan 11 | * 12 | */ 13 | public class SivanCacheRemoveListener implements ISivanCacheRemoveListener { 14 | 15 | private static final Log log = LogFactory.getLog(SivanCacheRemoveListener.class); 16 | 17 | @Override 18 | public void listen(ISivanCacheRemoveListenerContext context) { 19 | log.debug("Remove key: {}, value: {}, type: {}", 20 | context.key(), context.value(), context.type()); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/remove/SivanCacheRemoveListenerContext.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.remove; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheRemoveListenerContext; 5 | 6 | /** 7 | * 删除的监听器 8 | * @author sivan 9 | * 10 | */ 11 | public class SivanCacheRemoveListenerContext implements ISivanCacheRemoveListenerContext { 12 | 13 | /** 14 | * key 15 | * 16 | */ 17 | private K key; 18 | 19 | /** 20 | * 值 21 | * 22 | */ 23 | private V value; 24 | 25 | /** 26 | * 删除类型 27 | * 28 | */ 29 | private String type; 30 | 31 | /** 32 | * 新建实例 33 | * @param key 34 | * @param value 35 | * @return 结果 36 | * 37 | */ 38 | public static SivanCacheRemoveListenerContext newInstance() { 39 | return new SivanCacheRemoveListenerContext<>(); 40 | } 41 | 42 | @Override 43 | public K key() { 44 | return key; 45 | } 46 | 47 | public SivanCacheRemoveListenerContext key(K key) { 48 | this.key = key; 49 | return this; 50 | } 51 | 52 | @Override 53 | public V value() { 54 | return value; 55 | } 56 | 57 | public SivanCacheRemoveListenerContext value(V value) { 58 | this.value = value; 59 | return this; 60 | } 61 | 62 | @Override 63 | public String type() { 64 | return type; 65 | } 66 | 67 | public SivanCacheRemoveListenerContext type(String type) { 68 | this.type = type; 69 | return this; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/remove/SivanCacheRemoveListeners.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.remove; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheRemoveListener; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 缓存删除监听类 11 | * @author sivan 12 | * 13 | */ 14 | public class SivanCacheRemoveListeners { 15 | 16 | private SivanCacheRemoveListeners(){} 17 | 18 | /** 19 | * 默认监听类 20 | * @return 监听类列表 21 | * @param key 22 | * @param value 23 | * 24 | */ 25 | @SuppressWarnings("all") 26 | public static List> defaults() { 27 | List> listeners = new ArrayList<>(); 28 | listeners.add(new SivanCacheRemoveListener()); 29 | return listeners; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/slow/SivanCacheSlowListener.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.slow; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.xin.cache.api.ISivanCacheSlowListener; 7 | import com.xin.cache.api.ISivanCacheSlowListenerContext; 8 | import com.xin.cache.support.interceptor.common.SivanCacheInterceptorCost; 9 | 10 | /** 11 | * 慢日志监听类 12 | * @author sivan 13 | * 14 | */ 15 | public class SivanCacheSlowListener implements ISivanCacheSlowListener { 16 | 17 | private static final Log log = LogFactory.getLog(SivanCacheInterceptorCost.class); 18 | 19 | @Override 20 | public void listen(ISivanCacheSlowListenerContext context) { 21 | log.warn("[Slow] methodName: {}, params: {}, cost time: {}", 22 | context.methodName(), JSON.toJSON(context.params()), context.costTimeMills()); 23 | } 24 | 25 | @Override 26 | public long slowerThanMills() { 27 | return 1000L; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/slow/SivanCacheSlowListenerContext.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.slow; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheSlowListenerContext; 5 | 6 | /** 7 | * @author sivan 8 | * 9 | */ 10 | public class SivanCacheSlowListenerContext implements ISivanCacheSlowListenerContext { 11 | 12 | /** 13 | * 方法名称 14 | * 15 | */ 16 | private String methodName; 17 | 18 | /** 19 | * 参数信息 20 | * 21 | */ 22 | private Object[] params; 23 | 24 | /** 25 | * 方法结果 26 | * 27 | */ 28 | private Object result; 29 | 30 | /** 31 | * 开始时间 32 | * 33 | */ 34 | private long startTimeMills; 35 | 36 | /** 37 | * 结束时间 38 | * 39 | */ 40 | private long endTimeMills; 41 | 42 | /** 43 | * 消耗时间 44 | * 45 | */ 46 | private long costTimeMills; 47 | 48 | /** 49 | * 50 | * @return 实例 51 | */ 52 | public static SivanCacheSlowListenerContext newInstance() { 53 | return new SivanCacheSlowListenerContext(); 54 | } 55 | 56 | @Override 57 | public String methodName() { 58 | return methodName; 59 | } 60 | 61 | public SivanCacheSlowListenerContext methodName(String methodName) { 62 | this.methodName = methodName; 63 | return this; 64 | } 65 | 66 | @Override 67 | public Object[] params() { 68 | return params; 69 | } 70 | 71 | public SivanCacheSlowListenerContext params(Object[] params) { 72 | this.params = params; 73 | return this; 74 | } 75 | 76 | @Override 77 | public Object result() { 78 | return result; 79 | } 80 | 81 | public SivanCacheSlowListenerContext result(Object result) { 82 | this.result = result; 83 | return this; 84 | } 85 | 86 | @Override 87 | public long startTimeMills() { 88 | return startTimeMills; 89 | } 90 | 91 | public SivanCacheSlowListenerContext startTimeMills(long startTimeMills) { 92 | this.startTimeMills = startTimeMills; 93 | return this; 94 | } 95 | 96 | @Override 97 | public long endTimeMills() { 98 | return endTimeMills; 99 | } 100 | 101 | public SivanCacheSlowListenerContext endTimeMills(long endTimeMills) { 102 | this.endTimeMills = endTimeMills; 103 | return this; 104 | } 105 | 106 | @Override 107 | public long costTimeMills() { 108 | return costTimeMills; 109 | } 110 | 111 | public SivanCacheSlowListenerContext costTimeMills(long costTimeMills) { 112 | this.costTimeMills = costTimeMills; 113 | return this; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/slow/SivanCacheSlowListeners.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.slow; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheSlowListener; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 慢日志监听工具类 11 | * @author sivan 12 | * 13 | */ 14 | public final class SivanCacheSlowListeners { 15 | 16 | private SivanCacheSlowListeners(){} 17 | 18 | /** 19 | * 无 20 | * @return 监听类列表 21 | * 22 | */ 23 | public static List none() { 24 | return new ArrayList<>(); 25 | } 26 | 27 | /** 28 | * 默认实现 29 | * @return 默认 30 | * 31 | */ 32 | public static ISivanCacheSlowListener defaults() { 33 | return new SivanCacheSlowListener(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/listener/slow/package-info.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.listener.slow; -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/load/SivanCacheLoadAof.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.load; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.io.FileUtil; 5 | import com.github.houbb.heaven.util.lang.StringUtil; 6 | import com.github.houbb.heaven.util.lang.reflect.ReflectMethodUtil; 7 | import com.github.houbb.heaven.util.util.CollectionUtil; 8 | import com.github.houbb.log.integration.core.Log; 9 | import com.github.houbb.log.integration.core.LogFactory; 10 | import com.xin.cache.annotation.SivanCacheInterceptor; 11 | import com.xin.cache.api.ISivanCache; 12 | import com.xin.cache.api.ISivanCacheLoad; 13 | import com.xin.cache.core.SivanCache; 14 | import com.xin.cache.model.PersistAofEntry; 15 | 16 | import java.lang.reflect.Method; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * 加载策略-AOF文件模式 23 | * @author sivan 24 | * 25 | */ 26 | public class SivanCacheLoadAof implements ISivanCacheLoad { 27 | 28 | private static final Log log = LogFactory.getLog(SivanCacheLoadAof.class); 29 | 30 | /** 31 | * 方法缓存 32 | * 33 | * 暂时比较简单,直接通过方法判断即可,不必引入参数类型增加复杂度。 34 | * 35 | */ 36 | private static final Map METHOD_MAP = new HashMap<>(); 37 | 38 | static { 39 | Method[] methods = SivanCache.class.getMethods(); 40 | 41 | for(Method method : methods){ 42 | SivanCacheInterceptor cacheInterceptor = method.getAnnotation(SivanCacheInterceptor.class); 43 | 44 | if(cacheInterceptor != null) { 45 | // 暂时 46 | if(cacheInterceptor.aof()) { 47 | String methodName = method.getName(); 48 | 49 | METHOD_MAP.put(methodName, method); 50 | } 51 | } 52 | } 53 | 54 | } 55 | 56 | /** 57 | * 文件路径 58 | * 59 | */ 60 | private final String dbPath; 61 | 62 | public SivanCacheLoadAof(String dbPath) { 63 | this.dbPath = dbPath; 64 | } 65 | 66 | @Override 67 | public void load(ISivanCache cache) { 68 | List lines = FileUtil.readAllLines(dbPath); 69 | log.info("[load] 开始处理 path: {}", dbPath); 70 | if(CollectionUtil.isEmpty(lines)) { 71 | log.info("[load] path: {} 文件内容为空,直接返回", dbPath); 72 | return; 73 | } 74 | 75 | for(String line : lines) { 76 | if(StringUtil.isEmpty(line)) { 77 | continue; 78 | } 79 | 80 | // 执行 81 | // 简单的类型还行,复杂的这种反序列化会失败 82 | PersistAofEntry entry = JSON.parseObject(line, PersistAofEntry.class); 83 | 84 | final String methodName = entry.getMethodName(); 85 | final Object[] objects = entry.getParams(); 86 | 87 | final Method method = METHOD_MAP.get(methodName); 88 | // 反射调用 89 | ReflectMethodUtil.invoke(cache, method, objects); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/load/SivanCacheLoadDbJson.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.load; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | 5 | import com.github.houbb.heaven.util.io.FileUtil; 6 | import com.github.houbb.heaven.util.lang.ObjectUtil; 7 | import com.github.houbb.heaven.util.lang.StringUtil; 8 | import com.github.houbb.heaven.util.util.CollectionUtil; 9 | import com.github.houbb.log.integration.core.Log; 10 | import com.github.houbb.log.integration.core.LogFactory; 11 | import com.xin.cache.api.ISivanCache; 12 | import com.xin.cache.api.ISivanCacheLoad; 13 | import com.xin.cache.model.PersistRdbEntry; 14 | 15 | import java.util.List; 16 | 17 | /** 18 | * 加载策略-文件路径 19 | * @author sivan 20 | * 21 | */ 22 | public class SivanCacheLoadDbJson implements ISivanCacheLoad { 23 | 24 | private static final Log log = LogFactory.getLog(SivanCacheLoadDbJson.class); 25 | 26 | /** 27 | * 文件路径 28 | * 29 | */ 30 | private final String dbPath; 31 | 32 | public SivanCacheLoadDbJson(String dbPath) { 33 | this.dbPath = dbPath; 34 | } 35 | 36 | @Override 37 | public void load(ISivanCache cache) { 38 | List lines = FileUtil.readAllLines(dbPath); 39 | log.info("[load] 开始处理 path: {}", dbPath); 40 | if(CollectionUtil.isEmpty(lines)) { 41 | log.info("[load] path: {} 文件内容为空,直接返回", dbPath); 42 | return; 43 | } 44 | 45 | for(String line : lines) { 46 | if(StringUtil.isEmpty(line)) { 47 | continue; 48 | } 49 | 50 | // 执行 51 | // 简单的类型还行,复杂的这种反序列化会失败 52 | PersistRdbEntry entry = JSON.parseObject(line, PersistRdbEntry.class); 53 | 54 | K key = entry.getKey(); 55 | V value = entry.getValue(); 56 | Long expire = entry.getExpire(); 57 | 58 | cache.put(key, value); 59 | if(ObjectUtil.isNotNull(expire)) { 60 | cache.expireAt(key, expire); 61 | } 62 | } 63 | //nothing... 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/load/SivanCacheLoadNone.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.load; 2 | 3 | 4 | import com.xin.cache.api.ISivanCache; 5 | import com.xin.cache.api.ISivanCacheLoad; 6 | 7 | /** 8 | * 加载策略-无 9 | * @author sivan 10 | * 11 | */ 12 | public class SivanCacheLoadNone implements ISivanCacheLoad { 13 | 14 | @Override 15 | public void load(ISivanCache cache) { 16 | //nothing... 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/load/SivanCacheLoads.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.load; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheLoad; 5 | 6 | /** 7 | * 8 | * 加载策略工具类 9 | * @author sivan 10 | * 11 | */ 12 | public final class SivanCacheLoads { 13 | 14 | private SivanCacheLoads(){} 15 | 16 | /** 17 | * 无加载 18 | * @param key 19 | * @param value 20 | * @return 值 21 | * 22 | */ 23 | public static ISivanCacheLoad none() { 24 | return new SivanCacheLoadNone<>(); 25 | } 26 | 27 | /** 28 | * 文件 JSON 29 | * @param dbPath 文件路径 30 | * @param key 31 | * @param value 32 | * @return 值 33 | * 34 | */ 35 | public static ISivanCacheLoad dbJson(final String dbPath) { 36 | return new SivanCacheLoadDbJson<>(dbPath); 37 | } 38 | 39 | /** 40 | * AOF 文件加载模式 41 | * @param dbPath 文件路径 42 | * @param key 43 | * @param value 44 | * @return 值 45 | * 46 | */ 47 | public static ISivanCacheLoad aof(final String dbPath) { 48 | return new SivanCacheLoadAof<>(dbPath); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/map/Maps.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.map; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author sivan 8 | * 9 | */ 10 | public final class Maps { 11 | 12 | private Maps(){} 13 | 14 | /** 15 | * hashMap 实现策略 16 | * @param key 17 | * @param value 18 | * @return map 实现 19 | * 20 | */ 21 | public static Map hashMap() { 22 | return new HashMap<>(); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/InnerSivanCachePersist.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 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.ISivanCachePersist; 7 | 8 | import java.util.concurrent.Executors; 9 | import java.util.concurrent.ScheduledExecutorService; 10 | 11 | /** 12 | * 内部缓存持久化类 13 | * @author sivan 14 | * @param key 15 | * @param value 16 | * 17 | */ 18 | public class InnerSivanCachePersist { 19 | 20 | private static final Log log = LogFactory.getLog(InnerSivanCachePersist.class); 21 | 22 | /** 23 | * 缓存信息 24 | * 25 | */ 26 | private final ISivanCache cache; 27 | 28 | /** 29 | * 缓存持久化策略 30 | * 31 | */ 32 | private final ISivanCachePersist persist; 33 | 34 | /** 35 | * 线程执行类 36 | * 37 | */ 38 | private static final ScheduledExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); 39 | 40 | public InnerSivanCachePersist(ISivanCache cache, ISivanCachePersist persist) { 41 | this.cache = cache; 42 | this.persist = persist; 43 | 44 | // 初始化 45 | this.init(); 46 | } 47 | 48 | /** 49 | * 初始化 50 | * 51 | */ 52 | private void init() { 53 | EXECUTOR_SERVICE.scheduleAtFixedRate(new Runnable() { 54 | @Override 55 | public void run() { 56 | try { 57 | log.info("开始持久化缓存信息"); 58 | persist.persist(cache); 59 | log.info("完成持久化缓存信息"); 60 | } catch (Exception exception) { 61 | log.error("文件持久化异常", exception); 62 | } 63 | } 64 | }, persist.delay(), persist.period(), persist.timeUnit()); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/SivanCachePersistAdaptor.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 2 | 3 | 4 | import com.xin.cache.api.ISivanCache; 5 | import com.xin.cache.api.ISivanCachePersist; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * 缓存持久化-适配器模式 11 | * @author sivan 12 | * 13 | */ 14 | public class SivanCachePersistAdaptor implements ISivanCachePersist { 15 | 16 | /** 17 | * 持久化 18 | * key长度 key+value 19 | * 第一个空格,获取 key 的长度,然后截取 20 | * @param cache 缓存 21 | */ 22 | @Override 23 | public void persist(ISivanCache cache) { 24 | } 25 | 26 | @Override 27 | public long delay() { 28 | return this.period(); 29 | } 30 | 31 | @Override 32 | public long period() { 33 | return 1; 34 | } 35 | 36 | @Override 37 | public TimeUnit timeUnit() { 38 | return TimeUnit.SECONDS; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/SivanCachePersistAof.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 2 | 3 | import com.github.houbb.heaven.util.io.FileUtil; 4 | import com.github.houbb.heaven.util.lang.StringUtil; 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 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * 缓存持久化-AOF 持久化模式 15 | * @author sivan 16 | * 17 | */ 18 | public class SivanCachePersistAof extends SivanCachePersistAdaptor { 19 | 20 | private static final Log log = LogFactory.getLog(SivanCachePersistAof.class); 21 | 22 | /** 23 | * 缓存列表 24 | * 25 | */ 26 | private final List bufferList = new ArrayList<>(); 27 | 28 | /** 29 | * 数据持久化路径 30 | * 31 | */ 32 | private final String dbPath; 33 | 34 | public SivanCachePersistAof(String dbPath) { 35 | this.dbPath = dbPath; 36 | } 37 | 38 | /** 39 | * 持久化 40 | * key长度 key+value 41 | * 第一个空格,获取 key 的长度,然后截取 42 | * @param cache 缓存 43 | */ 44 | @Override 45 | public void persist(ISivanCache cache) { 46 | log.info("开始 AOF 持久化到文件"); 47 | // 1. 创建文件 48 | if(!FileUtil.exists(dbPath)) { 49 | FileUtil.createFile(dbPath); 50 | } 51 | // 2. 持久化追加到文件中 52 | FileUtil.append(dbPath, bufferList); 53 | 54 | // 3. 清空 buffer 列表 55 | bufferList.clear(); 56 | log.info("完成 AOF 持久化到文件"); 57 | } 58 | 59 | @Override 60 | public long delay() { 61 | return 1; 62 | } 63 | 64 | @Override 65 | public long period() { 66 | return 1; 67 | } 68 | 69 | @Override 70 | public TimeUnit timeUnit() { 71 | return TimeUnit.SECONDS; 72 | } 73 | 74 | /** 75 | * 添加文件内容到 buffer 列表中 76 | * @param json json 信息 77 | * 78 | */ 79 | public void append(final String json) { 80 | if(StringUtil.isNotEmpty(json)) { 81 | bufferList.add(json); 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/SivanCachePersistDbJson.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.io.FileUtil; 5 | import com.xin.cache.api.ISivanCache; 6 | import com.xin.cache.model.PersistRdbEntry; 7 | 8 | import java.nio.file.StandardOpenOption; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * 缓存持久化-db-基于 JSON 15 | * @author sivan 16 | * 17 | */ 18 | public class SivanCachePersistDbJson extends SivanCachePersistAdaptor { 19 | 20 | /** 21 | * 数据库路径 22 | * 23 | */ 24 | private final String dbPath; 25 | 26 | public SivanCachePersistDbJson(String dbPath) { 27 | this.dbPath = dbPath; 28 | } 29 | 30 | /** 31 | * 持久化 32 | * key长度 key+value 33 | * 第一个空格,获取 key 的长度,然后截取 34 | * @param cache 缓存 35 | */ 36 | @Override 37 | public void persist(ISivanCache cache) { 38 | Set> entrySet = cache.entrySet(); 39 | 40 | // 创建文件 41 | FileUtil.createFile(dbPath); 42 | // 清空文件 43 | FileUtil.truncate(dbPath); 44 | 45 | for(Map.Entry entry : entrySet) { 46 | K key = entry.getKey(); 47 | Long expireTime = cache.expire().expireTime(key); 48 | PersistRdbEntry persistRdbEntry = new PersistRdbEntry<>(); 49 | persistRdbEntry.setKey(key); 50 | persistRdbEntry.setValue(entry.getValue()); 51 | persistRdbEntry.setExpire(expireTime); 52 | 53 | String line = JSON.toJSONString(persistRdbEntry); 54 | FileUtil.write(dbPath, line, StandardOpenOption.APPEND); 55 | } 56 | } 57 | 58 | @Override 59 | public long delay() { 60 | return 5; 61 | } 62 | 63 | @Override 64 | public long period() { 65 | return 5; 66 | } 67 | 68 | @Override 69 | public TimeUnit timeUnit() { 70 | return TimeUnit.MINUTES; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/SivanCachePersistNone.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 2 | 3 | /** 4 | * 缓存持久化-无任何操作 5 | * @author sivan 6 | * 7 | */ 8 | public class SivanCachePersistNone extends SivanCachePersistAdaptor { 9 | } 10 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/persist/SivanCachePersists.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.persist; 2 | 3 | 4 | import com.xin.cache.api.ISivanCachePersist; 5 | 6 | /** 7 | * 缓存持久化工具类 8 | * @author sivan 9 | * 10 | */ 11 | public final class SivanCachePersists { 12 | 13 | private SivanCachePersists(){} 14 | 15 | /** 16 | * 无操作 17 | * @param key 18 | * @param value 19 | * @return 结果 20 | * 21 | */ 22 | public static ISivanCachePersist none() { 23 | return new SivanCachePersistNone<>(); 24 | } 25 | 26 | /** 27 | * DB json 操作 28 | * @param key 29 | * @param value 30 | * @param path 文件路径 31 | * @return 结果 32 | * 33 | */ 34 | public static ISivanCachePersist dbJson(final String path) { 35 | return new SivanCachePersistDbJson<>(path); 36 | } 37 | 38 | /** 39 | * AOF 持久化 40 | * @param key 41 | * @param value 42 | * @param path 文件路径 43 | * @return 结果 44 | * 45 | */ 46 | public static ISivanCachePersist aof(final String path) { 47 | return new SivanCachePersistAof<>(path); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/ISivanCacheProxy.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.proxy; 2 | 3 | /** 4 | * 缓存代理接口 5 | * @author sivan 6 | * 7 | */ 8 | public interface ISivanCacheProxy { 9 | 10 | /** 11 | * 获取代理实现 12 | * @return 代理 13 | * 14 | */ 15 | Object proxy(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/SivanCacheProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * async All rights reserved. 4 | */ 5 | 6 | package com.xin.cache.support.proxy; 7 | 8 | 9 | import com.github.houbb.heaven.util.lang.ObjectUtil; 10 | import com.xin.cache.api.ISivanCache; 11 | import com.xin.cache.support.proxy.cglib.CglibProxy; 12 | import com.xin.cache.support.proxy.dynamic.DynamicProxy; 13 | import com.xin.cache.support.proxy.none.NoneProxy; 14 | 15 | import java.lang.reflect.Proxy; 16 | 17 | /** 18 | *

代理信息

19 | * 20 | *
 Created: 2019/3/8 10:38 AM  
21 | *
 Project: async  
22 | * 23 | * @author houbinbin 24 | * 25 | */ 26 | public final class SivanCacheProxy { 27 | 28 | private SivanCacheProxy(){} 29 | 30 | /** 31 | * 获取对象代理 32 | * @param 泛型 key 33 | * @param 泛型 value 34 | * @param cache 对象代理 35 | * @return 代理信息 36 | * 37 | */ 38 | @SuppressWarnings("all") 39 | public static ISivanCache getProxy(final ISivanCache cache) { 40 | if(ObjectUtil.isNull(cache)) { 41 | return (ISivanCache) new NoneProxy(cache).proxy(); 42 | } 43 | 44 | final Class clazz = cache.getClass(); 45 | 46 | // 如果targetClass本身是个接口或者targetClass是JDK Proxy生成的,则使用JDK动态代理。 47 | // 参考 spring 的 AOP 判断 48 | if (clazz.isInterface() || Proxy.isProxyClass(clazz)) { 49 | return (ISivanCache) new DynamicProxy(cache).proxy(); 50 | } 51 | 52 | return (ISivanCache) new CglibProxy(cache).proxy(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/bs/ISivanCacheProxyBsContext.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.proxy.bs; 2 | 3 | 4 | import com.xin.cache.annotation.SivanCacheInterceptor; 5 | import com.xin.cache.api.ISivanCache; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * @author sivan 11 | * 12 | */ 13 | public interface ISivanCacheProxyBsContext { 14 | 15 | /** 16 | * 拦截器信息 17 | * @return 拦截器 18 | * 19 | */ 20 | SivanCacheInterceptor interceptor(); 21 | 22 | /** 23 | * 获取代理对象信息 24 | * @return 代理 25 | * 26 | */ 27 | ISivanCache target(); 28 | 29 | /** 30 | * 目标对象 31 | * @param target 对象 32 | * @return 结果 33 | * 34 | */ 35 | ISivanCacheProxyBsContext target(final ISivanCache target); 36 | 37 | /** 38 | * 参数信息 39 | * @return 参数信息 40 | * 41 | */ 42 | Object[] params(); 43 | 44 | /** 45 | * 方法信息 46 | * @return 方法信息 47 | * 48 | */ 49 | Method method(); 50 | 51 | /** 52 | * 方法执行 53 | * @return 执行 54 | * 55 | * @throws Throwable 异常信息 56 | */ 57 | Object process() throws Throwable; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/bs/SivanCacheProxyBs.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.proxy.bs; 2 | 3 | 4 | import com.xin.cache.annotation.SivanCacheInterceptor; 5 | import com.xin.cache.api.ISivanCache; 6 | import com.xin.cache.api.ISivanCacheInterceptor; 7 | import com.xin.cache.api.ISivanCachePersist; 8 | import com.xin.cache.support.interceptor.SivanCacheInterceptorContext; 9 | import com.xin.cache.support.interceptor.SivanCacheInterceptors; 10 | import com.xin.cache.support.persist.SivanCachePersistAof; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 代理引导类 16 | * @author sivan 17 | * 18 | */ 19 | public final class SivanCacheProxyBs { 20 | 21 | private SivanCacheProxyBs(){} 22 | 23 | /** 24 | * 代理上下文 25 | * 26 | */ 27 | private ISivanCacheProxyBsContext context; 28 | 29 | /** 30 | * 默认通用拦截器 31 | * 32 | * JDK 的泛型擦除导致这里不能使用泛型 33 | * 34 | */ 35 | @SuppressWarnings("all") 36 | private final List commonInterceptors = SivanCacheInterceptors.defaultCommonList(); 37 | 38 | /** 39 | * 默认刷新拦截器 40 | * 41 | */ 42 | @SuppressWarnings("all") 43 | private final List refreshInterceptors = SivanCacheInterceptors.defaultRefreshList(); 44 | 45 | /** 46 | * 持久化拦截器 47 | * 48 | */ 49 | @SuppressWarnings("all") 50 | private final ISivanCacheInterceptor persistInterceptors = SivanCacheInterceptors.aof(); 51 | 52 | /** 53 | * 驱除拦截器 54 | * 55 | */ 56 | @SuppressWarnings("all") 57 | private final ISivanCacheInterceptor evictInterceptors = SivanCacheInterceptors.evict(); 58 | 59 | /** 60 | * 新建对象实例 61 | * @return 实例 62 | * 63 | */ 64 | public static SivanCacheProxyBs newInstance() { 65 | return new SivanCacheProxyBs(); 66 | } 67 | 68 | public SivanCacheProxyBs context(ISivanCacheProxyBsContext context) { 69 | this.context = context; 70 | return this; 71 | } 72 | 73 | /** 74 | * 执行 75 | * @return 结果 76 | * 77 | * @throws Throwable 异常 78 | */ 79 | @SuppressWarnings("all") 80 | public Object execute() throws Throwable { 81 | //1. 开始的时间 82 | final long startMills = System.currentTimeMillis(); 83 | final ISivanCache cache = context.target(); 84 | SivanCacheInterceptorContext interceptorContext = SivanCacheInterceptorContext.newInstance() 85 | .startMills(startMills) 86 | .method(context.method()) 87 | .params(context.params()) 88 | .cache(context.target()) 89 | ; 90 | 91 | //1. 获取刷新注解信息 92 | SivanCacheInterceptor cacheInterceptor = context.interceptor(); 93 | this.interceptorHandler(cacheInterceptor, interceptorContext, cache, true); 94 | 95 | //2. 正常执行 96 | Object result = context.process(); 97 | 98 | final long endMills = System.currentTimeMillis(); 99 | interceptorContext.endMills(endMills).result(result); 100 | 101 | // 方法执行完成 102 | this.interceptorHandler(cacheInterceptor, interceptorContext, cache, false); 103 | return result; 104 | } 105 | 106 | /** 107 | * 拦截器执行类 108 | * @param cacheInterceptor 缓存拦截器 109 | * @param interceptorContext 上下文 110 | * @param cache 缓存 111 | * @param before 是否执行执行 112 | * 113 | */ 114 | @SuppressWarnings("all") 115 | private void interceptorHandler(SivanCacheInterceptor cacheInterceptor, 116 | SivanCacheInterceptorContext interceptorContext, 117 | ISivanCache cache, 118 | boolean before) { 119 | if(cacheInterceptor != null) { 120 | //1. 通用 121 | if(cacheInterceptor.common()) { 122 | for(ISivanCacheInterceptor interceptor : commonInterceptors) { 123 | if(before) { 124 | interceptor.before(interceptorContext); 125 | } else { 126 | interceptor.after(interceptorContext); 127 | } 128 | } 129 | } 130 | 131 | //2. 刷新 132 | if(cacheInterceptor.refresh()) { 133 | for(ISivanCacheInterceptor interceptor : refreshInterceptors) { 134 | if(before) { 135 | interceptor.before(interceptorContext); 136 | } else { 137 | interceptor.after(interceptorContext); 138 | } 139 | } 140 | } 141 | 142 | //3. AOF 追加 143 | final ISivanCachePersist cachePersist = cache.persist(); 144 | if(cacheInterceptor.aof() && (cachePersist instanceof SivanCachePersistAof)) { 145 | if(before) { 146 | persistInterceptors.before(interceptorContext); 147 | } else { 148 | persistInterceptors.after(interceptorContext); 149 | } 150 | } 151 | 152 | //4. 驱除策略更新 153 | if(cacheInterceptor.evict()) { 154 | if(before) { 155 | evictInterceptors.before(interceptorContext); 156 | } else { 157 | evictInterceptors.after(interceptorContext); 158 | } 159 | } 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/bs/SivanCacheProxyBsContext.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.proxy.bs; 2 | 3 | 4 | import com.xin.cache.annotation.SivanCacheInterceptor; 5 | import com.xin.cache.api.ISivanCache; 6 | 7 | import java.lang.reflect.Method; 8 | 9 | /** 10 | * 代理引导类上下文 11 | * @author sivan 12 | * 13 | */ 14 | public class SivanCacheProxyBsContext implements ISivanCacheProxyBsContext { 15 | 16 | /** 17 | * 目标 18 | * 19 | */ 20 | private ISivanCache target; 21 | 22 | /** 23 | * 入参 24 | * 25 | */ 26 | private Object[] params; 27 | 28 | /** 29 | * 方法 30 | * 31 | */ 32 | private Method method; 33 | 34 | /** 35 | * 拦截器 36 | * 37 | */ 38 | private SivanCacheInterceptor interceptor; 39 | 40 | /** 41 | * 新建对象 42 | * @return 对象 43 | * 44 | */ 45 | public static SivanCacheProxyBsContext newInstance(){ 46 | return new SivanCacheProxyBsContext(); 47 | } 48 | 49 | @Override 50 | public ISivanCache target() { 51 | return target; 52 | } 53 | 54 | @Override 55 | public SivanCacheProxyBsContext target(ISivanCache target) { 56 | this.target = target; 57 | return this; 58 | } 59 | 60 | @Override 61 | public Object[] params() { 62 | return params; 63 | } 64 | 65 | public SivanCacheProxyBsContext params(Object[] params) { 66 | this.params = params; 67 | return this; 68 | } 69 | 70 | @Override 71 | public Method method() { 72 | return method; 73 | } 74 | 75 | @Override 76 | public Object process() throws Throwable { 77 | return this.method.invoke(target, params); 78 | } 79 | 80 | public SivanCacheProxyBsContext method(Method method) { 81 | this.method = method; 82 | this.interceptor = method.getAnnotation(SivanCacheInterceptor.class); 83 | return this; 84 | } 85 | 86 | @Override 87 | public SivanCacheInterceptor interceptor() { 88 | return interceptor; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/cglib/CglibProxy.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.proxy.cglib; 2 | 3 | import com.xin.cache.api.ISivanCache; 4 | import com.xin.cache.support.proxy.ISivanCacheProxy; 5 | import com.xin.cache.support.proxy.bs.ISivanCacheProxyBsContext; 6 | import com.xin.cache.support.proxy.bs.SivanCacheProxyBs; 7 | import com.xin.cache.support.proxy.bs.SivanCacheProxyBsContext; 8 | import net.sf.cglib.proxy.Enhancer; 9 | import net.sf.cglib.proxy.MethodInterceptor; 10 | import net.sf.cglib.proxy.MethodProxy; 11 | 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * CGLIB 代理类 16 | * @author sivan 17 | * date 2019/3/7 18 | * 19 | */ 20 | public class CglibProxy implements MethodInterceptor, ISivanCacheProxy { 21 | 22 | /** 23 | * 被代理的对象 24 | */ 25 | private final ISivanCache target; 26 | 27 | public CglibProxy(ISivanCache target) { 28 | this.target = target; 29 | } 30 | 31 | @Override 32 | public Object intercept(Object o, Method method, Object[] params, MethodProxy methodProxy) throws Throwable { 33 | ISivanCacheProxyBsContext context = SivanCacheProxyBsContext.newInstance() 34 | .method(method).params(params).target(target); 35 | 36 | return SivanCacheProxyBs.newInstance().context(context).execute(); 37 | } 38 | 39 | @Override 40 | public Object proxy() { 41 | Enhancer enhancer = new Enhancer(); 42 | //目标对象类 43 | enhancer.setSuperclass(target.getClass()); 44 | enhancer.setCallback(this); 45 | //通过字节码技术创建目标对象类的子类实例作为代理 46 | return enhancer.create(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/dynamic/DynamicProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * async All rights reserved. 4 | */ 5 | 6 | package com.xin.cache.support.proxy.dynamic; 7 | 8 | 9 | import com.xin.cache.api.ISivanCache; 10 | import com.xin.cache.support.proxy.ISivanCacheProxy; 11 | import com.xin.cache.support.proxy.bs.ISivanCacheProxyBsContext; 12 | import com.xin.cache.support.proxy.bs.SivanCacheProxyBs; 13 | import com.xin.cache.support.proxy.bs.SivanCacheProxyBsContext; 14 | 15 | import java.lang.reflect.InvocationHandler; 16 | import java.lang.reflect.Method; 17 | import java.lang.reflect.Proxy; 18 | import java.util.concurrent.CompletionService; 19 | 20 | /** 21 | *

动态代理

22 | * 23 | * 1. 对于 executor 的抽象,使用 {@link CompletionService} 24 | * 2. 确保唯一初始化 executor,在任务执行的最后关闭 executor。 25 | * 3. 异步执行结果的获取,异常信息的获取。 26 | *
 Created: 2019/3/5 10:23 PM  
27 | *
 Project: async  
28 | * 29 | * @author houbinbin 30 | * 31 | */ 32 | public class DynamicProxy implements InvocationHandler, ISivanCacheProxy { 33 | 34 | /** 35 | * 被代理的对象 36 | */ 37 | private final ISivanCache target; 38 | 39 | public DynamicProxy(ISivanCache target) { 40 | this.target = target; 41 | } 42 | 43 | /** 44 | * 这种方式虽然实现了异步执行,但是存在一个缺陷: 45 | * 强制用户返回值为 Future 的子类。 46 | * 47 | * 如何实现不影响原来的值,要怎么实现呢? 48 | * @param proxy 原始对象 49 | * @param method 方法 50 | * @param args 入参 51 | * @return 结果 52 | * @throws Throwable 异常 53 | */ 54 | @Override 55 | @SuppressWarnings("all") 56 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 57 | ISivanCacheProxyBsContext context = SivanCacheProxyBsContext.newInstance() 58 | .method(method).params(args).target(target); 59 | return SivanCacheProxyBs.newInstance().context(context).execute(); 60 | } 61 | 62 | @Override 63 | public Object proxy() { 64 | // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 65 | InvocationHandler handler = new DynamicProxy(target); 66 | 67 | return Proxy.newProxyInstance(handler.getClass().getClassLoader(), 68 | target.getClass().getInterfaces(), handler); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/dynamic/package-info.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * async All rights reserved. 4 | */ 5 | 6 | /** 7 | *

动态代理

8 | * 9 | *
 Created: 2019-03-05 22:23  
10 | *
 Project: async  
11 | * 12 | * @author houbinbin 13 | */ 14 | package com.xin.cache.support.proxy.dynamic; -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/proxy/none/NoneProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * async All rights reserved. 4 | */ 5 | 6 | package com.xin.cache.support.proxy.none; 7 | 8 | 9 | import com.xin.cache.support.proxy.ISivanCacheProxy; 10 | 11 | import java.lang.reflect.InvocationHandler; 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | *

没有代理

16 | * 17 | *
 Created: 2019/3/5 10:23 PM  
18 | *
 Project: cache  
19 | * 20 | * @author houbinbin 21 | * 22 | */ 23 | public class NoneProxy implements InvocationHandler, ISivanCacheProxy { 24 | 25 | /** 26 | * 代理对象 27 | */ 28 | private final Object target; 29 | 30 | public NoneProxy(Object target) { 31 | this.target = target; 32 | } 33 | 34 | @Override 35 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 36 | return method.invoke(proxy, args); 37 | } 38 | 39 | /** 40 | * 返回原始对象,没有代理 41 | * @return 原始对象 42 | */ 43 | @Override 44 | public Object proxy() { 45 | return this.target; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/struct/lru/ILruMap.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.struct.lru; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheEntry; 5 | 6 | /** 7 | * LRU map 接口 8 | * @author sivan 9 | * 10 | */ 11 | public interface ILruMap { 12 | 13 | /** 14 | * 移除最老的元素 15 | * @return 移除的明细 16 | * 17 | */ 18 | ISivanCacheEntry removeEldest(); 19 | 20 | /** 21 | * 更新 key 的信息 22 | * @param key key 23 | * 24 | */ 25 | void updateKey(final K key); 26 | 27 | /** 28 | * 移除对应的 key 信息 29 | * @param key key 30 | * 31 | */ 32 | void removeKey(final K key); 33 | 34 | /** 35 | * 是否为空 36 | * @return 是否 37 | * 38 | */ 39 | boolean isEmpty(); 40 | 41 | /** 42 | * 是否包含元素 43 | * @param key 元素 44 | * @return 结果 45 | * 46 | */ 47 | boolean contains(final K key); 48 | 49 | } 50 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/struct/lru/impl/LruMapCircleList.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.struct.lru.impl; 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.ISivanCacheEntry; 8 | import com.xin.cache.exception.SivanCacheRuntimeException; 9 | import com.xin.cache.model.CircleListNode; 10 | import com.xin.cache.model.SivanCacheEntry; 11 | import com.xin.cache.support.struct.lru.ILruMap; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * 基于循环列表的实现 18 | * @author sivan 19 | * 20 | */ 21 | public class LruMapCircleList implements ILruMap { 22 | 23 | private static final Log log = LogFactory.getLog(LruMapCircleList.class); 24 | 25 | /** 26 | * 头结点 27 | * 28 | */ 29 | private CircleListNode head; 30 | 31 | /** 32 | * 映射 map 33 | * 34 | */ 35 | private Map> indexMap; 36 | 37 | public LruMapCircleList() { 38 | // 双向循环链表 39 | this.head = new CircleListNode<>(null); 40 | this.head.next(this.head); 41 | this.head.pre(this.head); 42 | 43 | indexMap = new HashMap<>(); 44 | } 45 | 46 | /** 47 | * 删除最老的元素 48 | * 49 | * (1)从 head.next 开始遍历,如果元素 accessFlag = 0,则直接移除 50 | * (2)如果 accessFlag=1,则设置其值为0,循环下一个节点。 51 | * 52 | * @return 结果 53 | * 54 | */ 55 | @Override 56 | public ISivanCacheEntry removeEldest() { 57 | //fast-fail 58 | if(isEmpty()) { 59 | log.error("当前列表为空,无法进行删除"); 60 | throw new SivanCacheRuntimeException("不可删除头结点!"); 61 | } 62 | 63 | // 从最老的元素开始,此处直接从 head.next 开始,后续可以考虑优化记录这个 key 64 | CircleListNode node = this.head; 65 | while (node.next() != this.head) { 66 | // 下一个元素 67 | node = node.next(); 68 | 69 | if(!node.accessFlag()) { 70 | // 未访问,直接淘汰 71 | K key = node.key(); 72 | 73 | this.removeKey(key); 74 | return SivanCacheEntry.of(key, node.value()); 75 | } else { 76 | // 设置当前 accessFlag = 0,继续下一个 77 | node.accessFlag(false); 78 | } 79 | } 80 | 81 | // 如果循环一遍都没找到,直接取第一个元素即可。 82 | CircleListNode firstNode = this.head.next(); 83 | return SivanCacheEntry.of(firstNode.key(), firstNode.value()); 84 | } 85 | 86 | /** 87 | * 放入元素 88 | * 89 | * 类似于 FIFO,直接放在队列的最后 90 | * 91 | * head==1==head 92 | * 加入元素: 93 | * 94 | * head==1==2==head 95 | * 96 | * (1)如果元素不存在,则直接插入。 97 | * 默认 accessFlag = 0; 98 | * (2)如果已经存在,则更新 accessFlag=1; 99 | * 100 | * @param key 元素 101 | * 102 | */ 103 | @Override 104 | public void updateKey(final K key) { 105 | CircleListNode node = indexMap.get(key); 106 | 107 | // 存在 108 | if(ObjectUtil.isNotNull(node)) { 109 | node.accessFlag(true); 110 | log.debug("节点已存在,设置节点访问标识为 true, key: {}", key); 111 | } else { 112 | // 不存在,则插入到最后 113 | node = new CircleListNode<>(key); 114 | 115 | CircleListNode tail = head.pre(); 116 | tail.next(node); 117 | node.pre(tail); 118 | node.next(head); 119 | head.pre(node); 120 | 121 | // 放入 indexMap 中,便于快速定位 122 | indexMap.put(key, node); 123 | log.debug("节点不存在,新增节点到链表中:{}", key); 124 | } 125 | } 126 | 127 | /** 128 | * 移除元素 129 | * 130 | * 1. 是否存在,不存在则忽略 131 | * 2. 存在则移除,从链表+map中移除 132 | * 133 | * head==1==2==head 134 | * 135 | * 删除 2 之后: 136 | * head==1==head 137 | * @param key 元素 138 | * 139 | */ 140 | @Override 141 | public void removeKey(final K key) { 142 | CircleListNode node = indexMap.get(key); 143 | if(ObjectUtil.isNull(node)) { 144 | log.warn("对应的删除信息不存在:{}", key); 145 | return; 146 | } 147 | 148 | CircleListNode pre = node.pre(); 149 | CircleListNode next = node.next(); 150 | 151 | //1-->(x2)-->3 直接移除2 152 | pre.next(next); 153 | next.pre(pre); 154 | indexMap.remove(key); 155 | 156 | log.debug("Key: {} 从循环链表中移除", key); 157 | } 158 | 159 | @Override 160 | public boolean isEmpty() { 161 | return indexMap.isEmpty(); 162 | } 163 | 164 | @Override 165 | public boolean contains(K key) { 166 | return indexMap.containsKey(key); 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /sivan-core/src/main/java/com/xin/cache/support/struct/lru/impl/LruMapDoubleList.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.support.struct.lru.impl; 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.ISivanCacheEntry; 8 | import com.xin.cache.exception.SivanCacheRuntimeException; 9 | import com.xin.cache.model.DoubleListNode; 10 | import com.xin.cache.model.SivanCacheEntry; 11 | import com.xin.cache.support.struct.lru.ILruMap; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * 基于双向列表的实现 18 | * @author sivan 19 | * 20 | */ 21 | public class LruMapDoubleList implements ILruMap { 22 | 23 | private static final Log log = LogFactory.getLog(LruMapDoubleList.class); 24 | 25 | /** 26 | * 头结点 27 | * 28 | */ 29 | private DoubleListNode head; 30 | 31 | /** 32 | * 尾巴结点 33 | * 34 | */ 35 | private DoubleListNode tail; 36 | 37 | /** 38 | * map 信息 39 | * 40 | * key: 元素信息 41 | * value: 元素在 list 中对应的节点信息 42 | * 43 | */ 44 | private Map> indexMap; 45 | 46 | public LruMapDoubleList() { 47 | this.indexMap = new HashMap<>(); 48 | this.head = new DoubleListNode<>(); 49 | this.tail = new DoubleListNode<>(); 50 | 51 | this.head.next(this.tail); 52 | this.tail.pre(this.head); 53 | } 54 | 55 | @Override 56 | public ISivanCacheEntry removeEldest() { 57 | // 获取尾巴节点的前一个元素 58 | DoubleListNode tailPre = this.tail.pre(); 59 | if(tailPre == this.head) { 60 | log.error("当前列表为空,无法进行删除"); 61 | throw new SivanCacheRuntimeException("不可删除头结点!"); 62 | } 63 | 64 | K evictKey = tailPre.key(); 65 | V evictValue = tailPre.value(); 66 | 67 | // 执行删除 68 | this.removeKey(evictKey); 69 | 70 | return SivanCacheEntry.of(evictKey, evictValue); 71 | } 72 | 73 | /** 74 | * 放入元素 75 | * 76 | * (1)删除已经存在的 77 | * (2)新元素放到元素头部 78 | * 79 | * @param key 元素 80 | * 81 | */ 82 | @Override 83 | public void updateKey(final K key) { 84 | //1. 执行删除 85 | this.removeKey(key); 86 | 87 | //2. 新元素插入到头部 88 | //head<->next 89 | //变成:head<->new<->next 90 | DoubleListNode newNode = new DoubleListNode<>(); 91 | newNode.key(key); 92 | 93 | DoubleListNode next = this.head.next(); 94 | this.head.next(newNode); 95 | newNode.pre(this.head); 96 | next.pre(newNode); 97 | newNode.next(next); 98 | 99 | //2.2 插入到 map 中 100 | indexMap.put(key, newNode); 101 | } 102 | 103 | /** 104 | * 移除元素 105 | * 106 | * 1. 获取 map 中的元素 107 | * 2. 不存在直接返回,存在执行以下步骤: 108 | * 2.1 删除双向链表中的元素 109 | * 2.2 删除 map 中的元素 110 | * 111 | * @param key 元素 112 | * 113 | */ 114 | @Override 115 | public void removeKey(final K key) { 116 | DoubleListNode node = indexMap.get(key); 117 | 118 | if(ObjectUtil.isNull(node)) { 119 | return; 120 | } 121 | 122 | // 删除 list node 123 | // A<->B<->C 124 | // 删除 B,需要变成: A<->C 125 | DoubleListNode pre = node.pre(); 126 | DoubleListNode next = node.next(); 127 | 128 | pre.next(next); 129 | next.pre(pre); 130 | 131 | // 删除 map 中对应信息 132 | this.indexMap.remove(key); 133 | log.debug("从 LruMapDoubleList 中移除 key: {}", key); 134 | } 135 | 136 | @Override 137 | public boolean isEmpty() { 138 | return indexMap.isEmpty(); 139 | } 140 | 141 | @Override 142 | public boolean contains(K key) { 143 | return indexMap.containsKey(key); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /sivan-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.xin 8 | Sivan_Cache 9 | 1.0-SNAPSHOT 10 | 11 | 12 | sivan-test 13 | 14 | 15 | 16 | 17 | org.apache.maven.plugins 18 | maven-compiler-plugin 19 | 20 | 8 21 | 8 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | com.github.houbb 31 | test-core 32 | 33 | 34 | com.xin 35 | sivan-api 36 | 1.0-SNAPSHOT 37 | test 38 | 39 | 40 | com.xin 41 | sivan-core 42 | 1.0-SNAPSHOT 43 | test 44 | 45 | 46 | 47 | 48 | 8 49 | 8 50 | UTF-8 51 | 52 | 53 | -------------------------------------------------------------------------------- /sivan-test/src/test/java/com/xin/cache/Test001.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache; 2 | 3 | import org.junit.Test; 4 | 5 | /** 6 | * @Author 不知名网友鑫 7 | * @Date 2023/11/3 8 | **/ 9 | 10 | public class Test001 { 11 | @Test 12 | public void test01(){ 13 | System.out.println("hello world"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /sivan-test/src/test/java/com/xin/cache/bs/SivanCacheBsTest.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.bs; 2 | 3 | 4 | import com.xin.cache.api.ISivanCache; 5 | import com.xin.cache.listener.MyRemoveListener; 6 | import com.xin.cache.listener.MySlowListener; 7 | import com.xin.cache.load.MySivanCacheLoad; 8 | import com.xin.cache.support.evict.SivanCacheEvicts; 9 | import com.xin.cache.support.load.SivanCacheLoads; 10 | import com.xin.cache.support.map.Maps; 11 | import com.xin.cache.support.persist.SivanCachePersists; 12 | import org.junit.Assert; 13 | import org.junit.Test; 14 | 15 | import java.util.concurrent.TimeUnit; 16 | 17 | /** 18 | * 缓存引导类测试 19 | * @author sivan 20 | * 21 | */ 22 | public class SivanCacheBsTest { 23 | 24 | /** 25 | * 大小指定测试 26 | * 27 | */ 28 | @Test 29 | public void helloTest() { 30 | ISivanCache cache = SivanCacheBs.newInstance() 31 | .size(2) 32 | .build(); 33 | 34 | cache.put("1", "1"); 35 | cache.put("2", "2"); 36 | cache.put("3", "3"); 37 | cache.put("4", "4"); 38 | 39 | Assert.assertEquals(2, cache.size()); 40 | System.out.println(cache.keySet()); 41 | } 42 | 43 | /** 44 | * 配置指定测试 45 | * 46 | */ 47 | @Test 48 | public void configTest() { 49 | ISivanCache cache = SivanCacheBs.newInstance() 50 | .map(Maps.hashMap()) 51 | .evict(SivanCacheEvicts.fifo()) 52 | .size(2) 53 | .build(); 54 | 55 | cache.put("1", "1"); 56 | cache.put("2", "2"); 57 | cache.put("3", "3"); 58 | cache.put("4", "4"); 59 | 60 | Assert.assertEquals(2, cache.size()); 61 | System.out.println(cache.keySet()); 62 | } 63 | 64 | /** 65 | * 过期测试 66 | * 67 | */ 68 | @Test 69 | public void expireTest() throws InterruptedException { 70 | ISivanCache cache = SivanCacheBs.newInstance() 71 | .size(3) 72 | .build(); 73 | 74 | cache.put("1", "1"); 75 | cache.put("2", "2"); 76 | 77 | cache.expire("1", 40); 78 | Assert.assertEquals(2, cache.size()); 79 | 80 | TimeUnit.MILLISECONDS.sleep(50); 81 | Assert.assertEquals(1, cache.size()); 82 | System.out.println(cache.keySet()); 83 | } 84 | 85 | /** 86 | * 缓存删除监听器 87 | * 88 | */ 89 | @Test 90 | public void cacheRemoveListenerTest() { 91 | ISivanCache cache = SivanCacheBs.newInstance() 92 | .size(1) 93 | .addRemoveListener(new MyRemoveListener()) 94 | .build(); 95 | 96 | cache.put("1", "1"); 97 | cache.put("2", "2"); 98 | } 99 | 100 | /** 101 | * 加载接口测试 102 | * 103 | */ 104 | @Test 105 | public void loadTest() { 106 | ISivanCache cache = SivanCacheBs.newInstance() 107 | .load(new MySivanCacheLoad()) 108 | .build(); 109 | 110 | Assert.assertEquals(2, cache.size()); 111 | } 112 | 113 | /** 114 | * 持久化接口测试 115 | * 116 | */ 117 | @Test 118 | public void persistRdbTest() throws InterruptedException { 119 | ISivanCache cache = SivanCacheBs.newInstance() 120 | .load(new MySivanCacheLoad()) 121 | .persist(SivanCachePersists.dbJson("1.rdb")) 122 | .build(); 123 | 124 | Assert.assertEquals(2, cache.size()); 125 | TimeUnit.SECONDS.sleep(5); 126 | } 127 | 128 | /** 129 | * 加载接口测试 130 | * 131 | */ 132 | @Test 133 | public void loadDbJsonTest() { 134 | ISivanCache cache = SivanCacheBs.newInstance() 135 | .load(SivanCacheLoads.dbJson("1.rdb")) 136 | .build(); 137 | 138 | Assert.assertEquals(2, cache.size()); 139 | } 140 | 141 | /** 142 | * 慢日志接口测试 143 | * 144 | */ 145 | @Test 146 | public void slowLogTest() { 147 | ISivanCache cache = SivanCacheBs.newInstance() 148 | .addSlowListener(new MySlowListener()) 149 | .build(); 150 | 151 | cache.put("1", "2"); 152 | cache.get("1"); 153 | } 154 | 155 | 156 | /** 157 | * 持久化 AOF 接口测试 158 | * 159 | */ 160 | @Test 161 | public void persistAofTest() throws InterruptedException { 162 | ISivanCache cache = SivanCacheBs.newInstance() 163 | .persist(SivanCachePersists.aof("1.aof")) 164 | .build(); 165 | 166 | cache.put("1", "1"); 167 | cache.expire("1", 10); 168 | cache.remove("2"); 169 | 170 | TimeUnit.SECONDS.sleep(1); 171 | } 172 | 173 | /** 174 | * 加载 AOF 接口测试 175 | * 176 | */ 177 | @Test 178 | public void loadAofTest() throws InterruptedException { 179 | ISivanCache cache = SivanCacheBs.newInstance() 180 | .load(SivanCacheLoads.aof("default.aof")) 181 | .build(); 182 | 183 | Assert.assertEquals(1, cache.size()); 184 | System.out.println(cache.keySet()); 185 | } 186 | 187 | 188 | /** 189 | * LRU 驱除策略测试 190 | * 191 | */ 192 | @Test 193 | public void lruEvictTest() throws InterruptedException { 194 | ISivanCache cache = SivanCacheBs.newInstance() 195 | .size(3) 196 | .evict(SivanCacheEvicts.lru()) 197 | .build(); 198 | 199 | cache.put("A", "hello"); 200 | cache.put("B", "world"); 201 | cache.put("C", "FIFO"); 202 | 203 | // 访问一次A 204 | cache.get("A"); 205 | cache.put("D", "LRU"); 206 | 207 | Assert.assertEquals(3, cache.size()); 208 | System.out.println(cache.keySet()); 209 | } 210 | 211 | @Test 212 | public void lruDoubleListMapTest() throws InterruptedException { 213 | ISivanCache cache = SivanCacheBs.newInstance() 214 | .size(3) 215 | .evict(SivanCacheEvicts.lruDoubleListMap()) 216 | .build(); 217 | 218 | cache.put("A", "hello"); 219 | cache.put("B", "world"); 220 | cache.put("C", "FIFO"); 221 | 222 | // 访问一次A 223 | cache.get("A"); 224 | cache.put("D", "LRU"); 225 | 226 | Assert.assertEquals(3, cache.size()); 227 | System.out.println(cache.keySet()); 228 | } 229 | 230 | /** 231 | * 基于 LinkedHashMap 实现 232 | * 233 | */ 234 | @Test 235 | public void lruLinkedHashMapTest() { 236 | ISivanCache cache = SivanCacheBs.newInstance() 237 | .size(3) 238 | .evict(SivanCacheEvicts.lruLinkedHashMap()) 239 | .build(); 240 | 241 | cache.put("A", "hello"); 242 | cache.put("B", "world"); 243 | cache.put("C", "FIFO"); 244 | 245 | // 访问一次A 246 | cache.get("A"); 247 | cache.put("D", "LRU"); 248 | 249 | Assert.assertEquals(3, cache.size()); 250 | System.out.println(cache.keySet()); 251 | } 252 | 253 | /** 254 | * 基于 LRU 2Q 实现 255 | * 256 | */ 257 | @Test 258 | public void lruQ2Test() { 259 | ISivanCache cache = SivanCacheBs.newInstance() 260 | .size(3) 261 | .evict(SivanCacheEvicts.lru2Q()) 262 | .build(); 263 | 264 | cache.put("A", "hello"); 265 | cache.put("B", "world"); 266 | cache.put("C", "FIFO"); 267 | 268 | // 访问一次A 269 | cache.get("A"); 270 | cache.put("D", "LRU"); 271 | 272 | Assert.assertEquals(3, cache.size()); 273 | System.out.println(cache.keySet()); 274 | } 275 | 276 | /** 277 | * 基于 LRU-2 实现 278 | * 279 | */ 280 | @Test 281 | public void lru2Test() { 282 | ISivanCache cache = SivanCacheBs.newInstance() 283 | .size(3) 284 | .evict(SivanCacheEvicts.lru2()) 285 | .build(); 286 | 287 | cache.put("A", "hello"); 288 | cache.put("B", "world"); 289 | cache.put("C", "FIFO"); 290 | 291 | // 访问一次A 292 | cache.get("A"); 293 | cache.put("D", "LRU"); 294 | 295 | Assert.assertEquals(3, cache.size()); 296 | System.out.println(cache.keySet()); 297 | } 298 | 299 | /** 300 | * 基于 LFU 实现 301 | * 302 | */ 303 | @Test 304 | public void lfuTest() { 305 | ISivanCache cache = SivanCacheBs.newInstance() 306 | .size(3) 307 | .evict(SivanCacheEvicts.lfu()) 308 | .build(); 309 | 310 | cache.put("A", "hello"); 311 | cache.put("B", "world"); 312 | cache.put("C", "FIFO"); 313 | 314 | // 访问一次A 315 | cache.get("A"); 316 | cache.put("D", "LRU"); 317 | 318 | Assert.assertEquals(3, cache.size()); 319 | System.out.println(cache.keySet()); 320 | } 321 | 322 | 323 | /** 324 | * 基于 clock 算法 实现 325 | * 326 | */ 327 | @Test 328 | public void clockTest() { 329 | ISivanCache cache = SivanCacheBs.newInstance() 330 | .size(3) 331 | .evict(SivanCacheEvicts.clock()) 332 | .build(); 333 | 334 | cache.put("A", "hello"); 335 | cache.put("B", "world"); 336 | cache.put("C", "FIFO"); 337 | 338 | // 访问一次A 339 | cache.get("A"); 340 | cache.put("D", "LRU"); 341 | 342 | Assert.assertEquals(3, cache.size()); 343 | System.out.println(cache.keySet()); 344 | } 345 | 346 | } 347 | -------------------------------------------------------------------------------- /sivan-test/src/test/java/com/xin/cache/listener/MyRemoveListener.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.listener; 2 | 3 | 4 | import com.xin.cache.api.ISivanCacheRemoveListener; 5 | import com.xin.cache.api.ISivanCacheRemoveListenerContext; 6 | 7 | /** 8 | * @author sivan 9 | * 10 | */ 11 | public class MyRemoveListener implements ISivanCacheRemoveListener { 12 | 13 | @Override 14 | public void listen(ISivanCacheRemoveListenerContext context) { 15 | System.out.println("【删除提示】可恶,我竟然被删除了!" + context.key()); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /sivan-test/src/test/java/com/xin/cache/listener/MySlowListener.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.listener; 2 | 3 | import com.xin.cache.api.ISivanCacheSlowListener; 4 | import com.xin.cache.api.ISivanCacheSlowListenerContext; 5 | 6 | /** 7 | * @author sivan 8 | * 9 | */ 10 | public class MySlowListener implements ISivanCacheSlowListener { 11 | 12 | @Override 13 | public void listen(ISivanCacheSlowListenerContext context) { 14 | System.out.println("【慢日志】name: " + context.methodName()); 15 | } 16 | 17 | @Override 18 | public long slowerThanMills() { 19 | return 0; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /sivan-test/src/test/java/com/xin/cache/load/MySivanCacheLoad.java: -------------------------------------------------------------------------------- 1 | package com.xin.cache.load; 2 | 3 | import com.xin.cache.api.ISivanCache; 4 | import com.xin.cache.api.ISivanCacheLoad; 5 | 6 | public class MySivanCacheLoad implements ISivanCacheLoad { 7 | 8 | @Override 9 | public void load(ISivanCache cache) { 10 | cache.put("1", "1"); 11 | cache.put("2", "2"); 12 | } 13 | 14 | } 15 | --------------------------------------------------------------------------------