0
点赞
收藏
分享

微信扫一扫

简单分享下Python的if

config

BloomFilterPenetrateProperties.java

布隆过滤器配置类

@Data
@ConfigurationProperties(prefix = BloomFilterPenetrateProperties.PREFIX)
public class BloomFilterPenetrateProperties {

    public static final String PREFIX = "framework.cache.redis.bloom-filter.default";

    /**
     * 布隆过滤器默认实例名称
     */
    private String name = "cache_penetration_bloom_filter";

    /**
     * 每个元素的预期插入量
     */
    private Long expectedInsertions = 64L;

    /**
     * 预期错误概率
     */
    private Double falseProbability = 0.03D;
}

 prefix = BloomFilterPenetrateProperties.PREFIX表示在配置文件中配置的信息与此类绑定,此类中设置的值都是默认值。

例如framework.cache.redis.bloom-filter.default.expected-insertions=1000

CacheAutoConfiguration.java

/**
 * 缓存配置自动装配
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
@AllArgsConstructor
@EnableConfigurationProperties({RedisDistributedProperties.class, BloomFilterPenetrateProperties.class})
public class CacheAutoConfiguration {

    private final RedisDistributedProperties redisDistributedProperties;

    /**
     * 创建 Redis Key 序列化器,可自定义 Key Prefix
     */
    @Bean
    public RedisKeySerializer redisKeySerializer() {
        String prefix = redisDistributedProperties.getPrefix();
        String prefixCharset = redisDistributedProperties.getPrefixCharset();
        return new RedisKeySerializer(prefix, prefixCharset);
    }

    /**
     * 防止缓存穿透的布隆过滤器
     */
    @Bean
    @ConditionalOnProperty(prefix = BloomFilterPenetrateProperties.PREFIX, name = "enabled", havingValue = "true")
    public RBloomFilter<String> cachePenetrationBloomFilter(RedissonClient redissonClient, BloomFilterPenetrateProperties bloomFilterPenetrateProperties) {
        RBloomFilter<String> cachePenetrationBloomFilter = redissonClient.getBloomFilter(bloomFilterPenetrateProperties.getName());
        cachePenetrationBloomFilter.tryInit(bloomFilterPenetrateProperties.getExpectedInsertions(), bloomFilterPenetrateProperties.getFalseProbability());
        return cachePenetrationBloomFilter;
    }

    @Bean
    // 静态代理模式: Redis 客户端代理类增强
    public StringRedisTemplateProxy stringRedisTemplateProxy(RedisKeySerializer redisKeySerializer,
                                                             StringRedisTemplate stringRedisTemplate,
                                                             RedissonClient redissonClient) {
        stringRedisTemplate.setKeySerializer(redisKeySerializer);
        return new StringRedisTemplateProxy(stringRedisTemplate, redisDistributedProperties, redissonClient);
    }
}

RedisDistributedProperties.java

/**
 * 分布式缓存配置
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
@Data
@ConfigurationProperties(prefix = RedisDistributedProperties.PREFIX)
public class RedisDistributedProperties {

    public static final String PREFIX = "framework.cache.redis";

    /**
     * Key 前缀
     */
    private String prefix = "";

    /**
     * Key 前缀字符集
     */
    private String prefixCharset = "UTF-8";

    /**
     * 默认超时时间
     */
    private Long valueTimeout = 30000L;

    /**
     * 时间单位
     */
    private TimeUnit valueTimeUnit = TimeUnit.MILLISECONDS;
}

core

CacheGetFilter(接口)

/**
 * 缓存过滤
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
@FunctionalInterface
public interface CacheGetFilter<T> {

    /**
     * 缓存过滤
     *
     * @param param 输出参数
     * @return {@code true} 如果输入参数匹配,否则 {@link Boolean#TRUE}
     */
    boolean filter(T param);
}

CacheGetIfAbsent(接口)

/**
 * 缓存查询为空
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
@FunctionalInterface
public interface CacheGetIfAbsent<T> {

    /**
     * 如果查询结果为空,执行逻辑
     */
    void execute(T param);
}

CacheLoader(接口)

/**
 * 缓存加载器
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
@FunctionalInterface
public interface CacheLoader<T> {

    /**
     * 加载缓存
     */
    T load();
}

toolkit

CacheUtil.java

/**
 * 缓存工具类
 * 公众号:马丁玩编程,回复:加群,添加马哥微信(备注:12306)获取项目资料
 */
public final class CacheUtil {

    private static final String SPLICING_OPERATOR = "_";

    /**
     * 构建缓存标识
     *
     * @param keys
     * @return
     */
    public static String buildKey(String... keys) {
        Stream.of(keys).forEach(each -> Optional.ofNullable(Strings.emptyToNull(each)).orElseThrow(() -> new RuntimeException("构建缓存 key 不允许为空")));
        return Joiner.on(SPLICING_OPERATOR).join(keys);
    }

    /**
     * 判断结果是否为空或空的字符串
     *
     * @param cacheVal
     * @return
     */
    public static boolean isNullOrBlank(Object cacheVal) {
        return cacheVal == null || (cacheVal instanceof String && Strings.isNullOrEmpty((String) cacheVal));
    }
}

FastJson2Util.java

public final class FastJson2Util {

    /**
     * 构建类型
     *
     * @param types
     * @return
     */
    public static Type buildType(Type... types) {
        ParameterizedTypeImpl beforeType = null;//定义了一个 beforeType 变量,用来保存前一次创建的 ParameterizedTypeImpl 实例。
        if (types != null && types.length > 0) {
            if (types.length == 1) {
                return new ParameterizedTypeImpl(new Type[]{null}, null, types[0]);
            }
            for (int i = types.length - 1; i > 0; i--) {
                beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]);
            }
        }
        return beforeType;
    }
}

 该工具类用来构建复杂的Type类型

Type... types: 这是一个可变参数列表,表示可以传入多个 Type 类型的参数。

if(types.length == 1)假如types数组只有一个元素就创建arameterizedTypeImpl 实例并返回

 ParameterizedTypeImpl 的构造方法接受三个参数:

  • 第一个参数是一个 Type[] 类型的数组,表示泛型参数。
  • 第二个参数是 ownerType,表示外部类的类型,对于一般的类型为 null
  • 第三个参数是 rawType,即最外层的原始类型(比如 ListMap 等)

for (int i = types.length - 1; i > 0; i--) {...}

如果传入的 types 数组有多个元素,倒序遍历 types 数组,从最后一个元素开始,依次向前构建嵌套的 ParameterizedTypeImpl 实例。

buildType 方法的核心作用是为 Type 类型提供递归嵌套支持。它通过 ParameterizedTypeImpl 实现了 Java 泛型类型的组合,从而能够表示复杂的泛型结构。这样构建的 Type 可以被 JSON.parseObject 等方法使用,以实现对复杂嵌套泛型类型的 JSON 反序列化。

Cache(接口)

此接口实际上定义了许多方法,是为了别的接口继承此接口,进而重写里面的方法,进行不同功能的实现,统一了标准

public interface Cache {

    /**
     * 获取缓存
     */
    <T> T get(@NotBlank String key, Class<T> clazz);

    /**
     * 放入缓存
     */
    void put(@NotBlank String key, Object value);

    /**
     * 如果 keys 全部不存在,则新增,返回 true,反之 false
     */
    Boolean putIfAllAbsent(@NotNull Collection<String> keys);

    /**
     * 删除缓存
     */
    Boolean delete(@NotBlank String key);

    /**
     * 删除 keys,返回删除数量
     */
    Long delete(@NotNull Collection<String> keys);

    /**
     * 判断 key 是否存在
     */
    Boolean hasKey(@NotBlank String key);

    /**
     * 获取缓存组件实例
     */
    Object getInstance();
}

 @NotNull注解表示参数不能为空

@noyBlank注解表示字符串参数不能为空,或者为空格以及制表符

    <T> T get(@NotBlank String key, Class<T> clazz);
Class<T> clazz表示你希望从缓存中得到什么类型的数据,使用的时候是

如果调用 get("user:1", User.class);,返回类型将是 User 类型的实例。

DistributedCache(接口)

实现分布式缓存,继承了Cache接口,重写了里面的方法,对基本的操作进行了扩展,比如安全获取以及等等,使用到了CacheLoader,CacheGetFilter,CacheGetIfAbsent

public interface DistributedCache extends Cache {

    /**
     * 获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     */
    <T> T get(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout);

    /**
     * 获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     */
    <T> T get(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存击穿、缓存雪崩场景,适用于不被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存击穿、缓存雪崩场景,适用于不被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, RBloomFilter<String> bloomFilter);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit, RBloomFilter<String> bloomFilter);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,并通过 {@link CacheGetFilter} 解决布隆过滤器无法删除问题,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, RBloomFilter<String> bloomFilter, CacheGetFilter<String> cacheCheckFilter);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,并通过 {@link CacheGetFilter} 解决布隆过滤器无法删除问题,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit, RBloomFilter<String> bloomFilter, CacheGetFilter<String> cacheCheckFilter);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,并通过 {@link CacheGetFilter} 解决布隆过滤器无法删除问题,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout,
                  RBloomFilter<String> bloomFilter, CacheGetFilter<String> cacheCheckFilter, CacheGetIfAbsent<String> cacheGetIfAbsent);

    /**
     * 以一种"安全"的方式获取缓存,如查询结果为空,调用 {@link CacheLoader} 加载缓存
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,并通过 {@link CacheGetFilter} 解决布隆过滤器无法删除问题,适用于被外部直接调用的接口
     */
    <T> T safeGet(@NotBlank String key, Class<T> clazz, CacheLoader<T> cacheLoader, long timeout, TimeUnit timeUnit,
                  RBloomFilter<String> bloomFilter, CacheGetFilter<String> cacheCheckFilter, CacheGetIfAbsent<String> cacheGetIfAbsent);

    /**
     * 放入缓存,自定义超时时间
     */
    void put(@NotBlank String key, Object value, long timeout);

    /**
     * 放入缓存,自定义超时时间
     */
    void put(@NotBlank String key, Object value, long timeout, TimeUnit timeUnit);

    /**
     * 放入缓存,自定义超时时间
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,适用于被外部直接调用的接口
     */
    void safePut(@NotBlank String key, Object value, long timeout, RBloomFilter<String> bloomFilter);

    /**
     * 放入缓存,自定义超时时间,并将 key 加入步隆过滤器。极大概率通过此方式防止:缓存穿透、缓存击穿、缓存雪崩
     * 通过此方式防止程序中可能出现的:缓存穿透、缓存击穿以及缓存雪崩场景,需要客户端传递布隆过滤器,适用于被外部直接调用的接口
     */
    void safePut(@NotBlank String key, Object value, long timeout, TimeUnit timeUnit, RBloomFilter<String> bloomFilter);

    /**
     * 统计指定 key 的存在数量
     */
    Long countExistingKeys(@NotNull String... keys);
}

RedisKeySerializer.java

StringRedisTemplateProxy.java

public class StringRedisTemplateProxy implements DistributedCache {

实现了DistributedCache接口,然后重写了其中的方法

@RequiredArgsConstructor

    private final StringRedisTemplate stringRedisTemplate;
    private final RedisDistributedProperties redisProperties;
    private final RedissonClient redissonClient;

加上注解对需要使用到的实例进行了引入

private static final String LUA_PUT_IF_ALL_ABSENT_SCRIPT_PATH = "lua/putIfAllAbsent.lua";
    private static final String SAFE_GET_DISTRIBUTED_LOCK_KEY_PREFIX = "safe_get_distributed_lock_get:";

 第一个是lua脚本路径

现在对每个方法进行详细的讲解

第一个(get)

@Override
    public <T> T get(String key, Class<T> clazz) {
        String value = stringRedisTemplate.opsForValue().get(key);
        if (String.class.isAssignableFrom(clazz)) {
            return (T) value;
        }
        return JSON.parseObject(value, FastJson2Util.buildType(clazz));
    }

是从redis中获取指定Key对应的值

if (String.class.isAssignableFrom(clazz))判断目标类型 clazz 是否为 String 类型或其子类。如果是,则直接将缓存值 value 作为字符串返回。

假如不是String类型或者其子类的话,SON.parseObject(value, FastJson2Util.buildType(clazz)) 是在使用 FastJSON 库将 JSON 字符串 value 反序列化为一个特定的 Java 类型 clazz 的对象

第二个(put)

@Override
    public void put(String key, Object value) {
        put(key, value, redisProperties.getValueTimeout());
    }

@Override
    public void put(String key, Object value, long timeout) {
        put(key, value, timeout, redisProperties.getValueTimeUnit());
    }

@Override
    public void put(String key, Object value, long timeout, TimeUnit timeUnit) {
        String actual = value instanceof String ? (String) value : JSON.toJSONString(value);
        stringRedisTemplate.opsForValue().set(key, actual, timeout, timeUnit);
    }

此方法是放入缓存

instanceof 运算符用于测试一个对象是否是某个特定类或其子类的实例,或者是否实现了某个接口

设置过期时间以及时间单位

 第三个(putIfAllAbsent)

@Override
    public Boolean putIfAllAbsent(@NotNull Collection<String> keys) {
        DefaultRedisScript<Boolean> actual = Singleton.get(LUA_PUT_IF_ALL_ABSENT_SCRIPT_PATH, () -> {
            DefaultRedisScript redisScript = new DefaultRedisScript();
            redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(LUA_PUT_IF_ALL_ABSENT_SCRIPT_PATH)));
            redisScript.setResultType(Boolean.class);
            return redisScript;
        });
        Boolean result = stringRedisTemplate.execute(actual, Lists.newArrayList(keys), redisProperties.getValueTimeout().toString());
        return result != null && result;
    }

 该方法名为 putIfAllAbsent,它接受一个 Collection<String> 类型的参数 keys,表示要放入缓存的键。该方法返回一个 Boolean 类型的值,表示操作的结果。

 这个 putIfAllAbsent 方法的主要功能是尝试将一组键放入 Redis 中,仅当所有键都不存在时才会执行插入操作。它通过 Lua 脚本实现了原子性,确保在高并发场景下的安全性。这种方式能够有效防止缓存穿透问题,并提高性能,因为它减少了与 Redis 的交互次数。

第四个(delete) 

@Override
    public Boolean delete(String key) {
        return stringRedisTemplate.delete(key);
    }

    @Override
    public Long delete(Collection<String> keys) {
        return stringRedisTemplate.delete(keys);
    }

分别是在redis中删除键和键的集合

举报

相关推荐

0 条评论