Commit 612a46be by 仲光辉

feat: add customer annotation cache reference.

parent d2d81138
...@@ -102,7 +102,7 @@ cache: `redis` ...@@ -102,7 +102,7 @@ cache: `redis`
}.getType()); }.getType());
} else { } else {
// 数据没有缓存 // 数据没有缓存
// TODO 缓存击穿 分布式锁 etc. Redisson // TODO 缓存击穿 分布式锁 etc. Redisson 可以参阅 cn.dankal.share.cache.reference
Provinces provinces = this.getProvincesByProvinces(provincesId); Provinces provinces = this.getProvincesByProvinces(provincesId);
Optional<Provinces> provincesOptional; Optional<Provinces> provincesOptional;
if (BeanUtil.isNotEmpty(provinces)) { if (BeanUtil.isNotEmpty(provinces)) {
...@@ -276,6 +276,10 @@ url: GET http://localhost:9102/test/440000 ...@@ -276,6 +276,10 @@ url: GET http://localhost:9102/test/440000
#### 小示例 #### 小示例
##### 第三方依赖
> [hutool-bloomFilter](https://mvnrepository.com/artifact/cn.hutool/hutool-bloomFilter)
##### 数据初始化 ##### 数据初始化
```java ```java
...@@ -310,7 +314,7 @@ url: GET http://localhost:9102/test/440000 ...@@ -310,7 +314,7 @@ url: GET http://localhost:9102/test/440000
##### 小提示 ##### 小提示
> 示例 只是单纯的对 `BitMapBloomFilter`进行初数化,我们还需考虑其数据更新.我们可以在 Provinces进行增删的时候手动对 BitMapBloomFilter`进行更新。当然我们也可以通过订阅 诸如`TiDB`的`Ticdc`来实现`BitMapBloomFilter`的数据更新 > 示例 只是单纯的对 `BitMapBloomFilter`进行初数化,我们还需考虑其数据更新.我们可以在 Provinces进行增删的时候手动对 `BitMapBloomFilter`进行更新。当然我们也可以通过订阅 诸如`TiDB`的`Ticdc`来实现`BitMapBloomFilter`的数据更新
### 解决方案示例 ### 解决方案示例
......
package cn.dankal.share.cache.reference.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
/**
* 条件缓存
*
* @author ZGH.MercyModest
* @version V1.0.0
* @create 2021-03-19
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheCondition {
/**
* Object条件 class
* <pre>
* 如果条件是基本数据类型,则直接顺序指定 {@link #conditionsValues()} 即可
* </pre>
*/
Class<?> objectConditionClass() default Object.class;
/**
* 条件对象所在方法参数的索引
*
*/
int objectConditionParameterIndex() default 0;
/**
* 条件 属性名称 {@link Field#getName()}
* <pre>
* 元素顺序需要和 {@link #conditionsValues()} 保持一致
* 如果条件是基本数据类型,则直接顺序指定 {@link #conditionsValues()} 即可
* </pre>
*/
String[] conditionsFieldNames() default {};
/**
* 条件属性值 {@link Field#get(Object)}
* <pre>
* 当使用 {@link #objectConditionClass()} 时 元素顺序需要和 {@link #conditionsFieldNames()} 保持一致
* 如果是基本数据类型,则只需要和方法参数保持一致即可.
* </pre>
*/
String[] conditionsValues();
}
package cn.dankal.share.cache.reference.cache;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 对象缓存key: 缓存key 的构成需要从对象中获取时,使用当前注解
* 此注解的对于指定 key 的优先级高于 {@link RedisCache#cacheKey()} {@link RedisCache#dynamicCacheKeyPattern()}
*
* @author ZGH.MercyModest
* @version V1.0.0
* @create 2021-03-19
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectCacheKey {
/**
* 对象 类型
*/
Class<?> objectClass();
/**
* 对象所在参数索引
*/
int objectParameterIndex() default 0;
/**
* 动态缓存 key pattern
* <pre>
* 动态 缓存 key pattern {@link StrUtil#format(CharSequence, Object...)}
* 占位符顺序需要和 {@link #fieldNames()} 一致
* </pre>
*/
String dynamicCacheKeyPattern();
/**
* 参与动态缓存 key 构成的属性名称
* <pre>
* 顺序需要和 {@link #dynamicCacheKeyPattern()} 保持一致
* </pre>
*/
String[] fieldNames();
}
package cn.dankal.share.cache.reference.cache;
import cn.hutool.core.util.StrUtil;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* ObjectDistributeLockKey 分布式锁动态key: pattern value 从对象中获取
* 此注解的对于指定 key 的优先级高于 {@link RedisCache#distributedLockKey()} ()} {@link RedisCache#dynamicDistributedLockKeyPattern()}
*
* @author ZGH.MercyModest
* @version V1.0.0
* @create 2021/03/21
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ObjectDistributeLockKey {
/**
* 对象 类型
*/
Class<?> objectClass();
/**
* 对象所在参数索引
*/
int objectParameterIndex() default 0;
/**
* 动态缓存 key pattern
* <pre>
* 动态 缓存 key pattern {@link StrUtil#format(CharSequence, Object...)}
* 占位符顺序需要和 {@link #fieldNames()} 一致
* </pre>
*/
String dynamicDistributeLockKeyPattern();
/**
* 参与动态缓存 key 构成的属性名称
* <pre>
* 顺序需要和 {@link #dynamicDistributeLockKeyPattern()} 保持一致
* </pre>
*/
String[] fieldNames();
}
package cn.dankal.share.cache.reference.cache;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
/**
* redis 缓存注解
* <p>
* 在需要使用 redis 缓存的 方法上 使用 当前注解
* </p>
*
* @author ZGH.MercyModest
* @version V1.0.0
* @create 2021-02-26
* @see RedisCacheAspect
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisCache {
/**
* 缓存 key
* <p>
* dynamicCacheKey=false 时,直接使用 cacheKey 作为缓存可以
* </p>
*
* @return 缓存 key
*/
String cacheKey() default "";
/**
* 是否使用 动态 缓存key
* <p>
* etc. product_item_13245253625232
* </p>
*
* @return 是否使用 动态 缓存key
*/
boolean dynamicCacheKey() default true;
/**
* 动态 缓存 key pattern etc. "product_item_{}"
* <pre>
* dynamicCacheKey=true
* 具体请参阅
* cn.hutool.core.util.StrUtil#format(java.lang.CharSequence, java.lang.Object...)
* </pre>
*
* @return 动态 缓存 key pattern etc. "product_item_{}"
*/
String dynamicCacheKeyPattern() default "";
/**
* 动态缓存 key 动态值所在方法参数的位置索引
* <pre>
* {@code
* dynamicCacheKey=true
* etc.
*
* dynamicCacheKeyParameterIndexArray = {0}
* dynamicCacheKeyPattern=product_item_{}
*
* public Product getProduct(Integer productId,...){
* ... ...
* }
*
* getProduct(1324536795953)
* the full cache key : product_item_1324536795953
* }
* </pre>
*
* @return 动态缓存 key 动态值所在方法参数的位置索引
*/
int[] dynamicCacheKeyParameterIndexArray() default {0};
/**
* 缓存 过期时间
* <p>
* 如果 cacheTimeout 小于等于0 则表示缓存属于永久缓存
* <br/>
* 当且仅当 cacheTimeout 的值 大于 0 时 当前配置才生效
* </p>
*
* @return 缓存 过期时间
*/
long cacheTimeout() default 0;
/**
* 缓存过期时间单位
*
* @return 缓存过期时间单位
*/
TimeUnit cacheTimeoutUnit() default TimeUnit.MINUTES;
/**
* 为了避免缓存穿透,我们需要对空值进行缓存
* 空值缓存 过期时间 (单位: 分钟)
*
* @return 空值缓存 过期时间
*/
long emptyObjectCacheTimeout() default 5;
/**
* 是否使用分布式锁
*
* @return 是否使用分布式锁
*/
boolean useDistributeLock() default true;
/**
* 分布式锁 key
* <p>
* 当且仅当 <code>useDistributeLock=true</code>时,当前配置才会生效(使用)
* <br/>
* distributedLockKey 不能为空
* </p>
*
* @return 分布式锁 key
*/
String distributedLockKey() default "";
/**
* 是否使用 动态 distributedLockKey
* <pre>
* distributedLockKey=false
* 直接使用 distributedLockKey 作为 分布式锁 的 key
*
* distributedLockKey=true
* dynamicDistributedLockKeyPattern 和 dynamicDistributedLockKeyParameterIndexArray 动态生成分布式锁 的 key
* </pre>
*
* @return boolean
*/
boolean dynamicDistributedLockKey() default true;
/**
* 动态 分布式锁 key pattern etc. "product_item_{}"
* <pre>
* dynamicDistributedLockKey=true
* cn.hutool.core.util.StrUtil#format(java.lang.CharSequence, java.lang.Object...)
* </pre>
*
* @return 动态 分布式锁 key pattern etc. "product_item_{}"
*/
String dynamicDistributedLockKeyPattern() default "";
/**
* 动态分布式锁 key 动态值所在方法参数的位置索引 数组
* <pre>
* <text>
* dynamicDistributedLockKey=true
* etc.
* dynamicDistributedLockKeyParameterIndexArray ={0}
* dynamicDistributedLockKeyPattern=product_item_{}
* {@code
* public Product getProduct(Integer productId,...){
* ... ...
* }
* }
* getProduct(1324536795953)
* the full distributed lock key : product_item_1324536795953
* </text>
* </pre>
*
* @return 动态分布式锁 key 动态值所在方法参数的位置索引 数组
*/
int[] dynamicDistributedLockKeyParameterIndexArray() default {0};
/**
* 尝试获取 redisson lock 等待的时间
* <p>
* 当且仅当 <code>isUseDistributeLock=true</code>时,当前配置才会生效(使用)
* <br/>
* 此值只用于避免 redis 服务器网络延迟,不易设置过大 切切
* </p>
*
* @return 尝试获取 redisson lock 等待的时间
*/
long redissonTryLockWaitTime() default 120;
/**
* 持有 redisson lock 的时间
* <p>
* 当且仅当 <code>isUseDistributeLock=true</code>时,当前配置才会生效(使用)
* </p>
*
* @return 持有 redisson lock 的时间
*/
long redissonLockLeaseTime() default 230;
/**
* redisson lock 时间单位
* <p>
* <text>
* redissonTryLockWaitTime
* redissonLockLeaseTime
* </text>
* </p>
*
* @return redisson lock 时间单位
*/
TimeUnit redissonLockTimeunit() default TimeUnit.MILLISECONDS;
}
package cn.dankal.share.cache.reference.config;
import cn.hutool.core.util.StrUtil;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* RedissonBaseConfig
* <br/>
*
* @author ZGH.MercyModest
* @version V1.0.0
* @create 2021/02/26
* @copyright 蛋壳创意科技 - www.dankal.cn
*/
@Configuration
public class RedissonBaseConfig {
/**
* RedisProperties
*/
private final RedisProperties redisProperties;
/**
* redisson redis host prefix
*/
private final static String ADDRESS_PREFIX = "redis://";
public RedissonBaseConfig(RedisProperties redisProperties) {
this.redisProperties = redisProperties;
}
@Bean(destroyMethod ="shutdown")
public RedissonClient redissonClient() {
Config config = new Config();
// 验证是否配置了 redis host/ redis nodes
String redisHost = redisProperties.getHost();
RedisProperties.Cluster propertiesCluster = redisProperties.getCluster();
boolean checkRedisHost = StrUtil.isBlank(redisHost) && (propertiesCluster == null || CollectionUtils.isEmpty(propertiesCluster.getNodes()));
if (checkRedisHost) {
throw new IllegalArgumentException("需要配置 redis.host 或 redis.cluster.nodes");
}
if (null != propertiesCluster && !CollectionUtils.isEmpty(propertiesCluster.getNodes())) {
// redis cluster
config.useClusterServers()
.addNodeAddress(this.transferRedissonRedisNode(propertiesCluster.getNodes()))
.setPassword(redisProperties.getPassword())
.setTimeout((int) redisProperties.getTimeout().toMillis());
} else {
// redis singleton
config.useSingleServer()
.setAddress(ADDRESS_PREFIX + redisHost + ":" + redisProperties.getPort())
.setTimeout((int) redisProperties.getTimeout().toMillis())
.setPassword(redisProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 将 node list 转化为 redisson redis cluster config
* <p>
* 127.0.0.0:6379 --> redis://127.0.0.1:6379
* </p>
*
* @param redisNodeList redisNodeList
* @return string []
*/
private String[] transferRedissonRedisNode(List<String> redisNodeList) {
String[] resultArray = new String[redisNodeList.size()];
for (int i = 0; i < resultArray.length; i++) {
resultArray[i] = ADDRESS_PREFIX + redisNodeList.get(i);
}
return resultArray;
}
}
...@@ -108,7 +108,7 @@ public class IProvincesServiceImpl extends ServiceImpl<ProvincesMapper, Province ...@@ -108,7 +108,7 @@ public class IProvincesServiceImpl extends ServiceImpl<ProvincesMapper, Province
}.getType()); }.getType());
} else { } else {
// 数据没有缓存 // 数据没有缓存
// TODO 缓存击穿 分布式锁 etc. Redisson // TODO 缓存击穿 分布式锁 etc. Redisson 可以参阅 cn.dankal.share.cache.reference
Provinces provinces = this.getProvincesByProvinces(provincesId); Provinces provinces = this.getProvincesByProvinces(provincesId);
Optional<Provinces> provincesOptional; Optional<Provinces> provincesOptional;
if (BeanUtil.isNotEmpty(provinces)) { if (BeanUtil.isNotEmpty(provinces)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment