当前位置: 首页>后端>正文

写一个简单的动态配置开关

前言

当我们用srping cloud config做配置中心的时候由于缺少动态修改配置的功能,所以无法做到即使的对一些代码流程做控制,每次修改配置都需要重启服务从git拉取新的配置,这样非常不方便所以我们需要一个轻量级的配置开关,需要能做到不重启服务就可以动态的修改配置。

设计

基于目前不是很复杂的场景,我的想法是设计一个utils。各种初始化的配置,直接由静态代码块初始化到一个map即可,首次调用时我们把配置从java内存加载到redis,之后的调用只要redis有值就从redis获取,如果redis没值就从静态map中获取。之后只需要修改redis里面的值就可以做到动态的修改配置了。

编码

实现@Cacheable注解

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {

    @Value("#{${cachetime.config}}")
    private Map<String, Integer> cacheTimeConfig;

    @Bean
    //当容器不存在RedisTemplate时,自定义RedisTemplate
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        //使用fastjson序列化
        FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
        // value值的序列化采用fastJsonRedisSerializer
        template.setValueSerializer(fastJsonRedisSerializer);
        template.setHashValueSerializer(fastJsonRedisSerializer);
        // key的序列化采用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    //当容器不存在StringRedisTemplate时,自定义StringRedisTemplate
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 设置springCache的过期时间
     */
    @Bean
    // 定义一个方法,用于创建RedisCacheManager实例,参数为RedisConnectionFactory对象
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 创建一个GenericFastJsonRedisSerializer实例,用于序列化和反序列化缓存数据到JSON格式
        GenericFastJsonRedisSerializer jsonRedisSerializer = new GenericFastJsonRedisSerializer();

        // 创建一个RedisSerializationContext.SerializationPair对象,指定使用jsonRedisSerializer作为值的序列化器
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);

        // 获取RedisCacheConfiguration的默认配置,并进行设置
        RedisCacheConfiguration defaultCacheConfig =
                RedisCacheConfiguration.defaultCacheConfig()
                        // 禁止缓存空值
                        .disableCachingNullValues()
                        // 设置缓存值的序列化方式为之前定义的pair
                        .serializeValuesWith(pair);

        // 创建一个非锁定Redis缓存写入器,与给定的RedisConnectionFactory关联
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);

        // 使用ImmutableSet.Builder构建器初始化一个不可变集合,用于存储自定义缓存名称
        ImmutableSet.Builder<String> cacheNames = ImmutableSet.builder();

        // 使用ImmutableMap.Builder构建器初始化一个不可变映射,用于存储每个缓存名称对应的配置
        ImmutableMap.Builder<String, RedisCacheConfiguration> cacheConfig = ImmutableMap.builder();

        // 遍历从properties文件中注入的cacheTimeConfig,该map存储了各个缓存名称及其过期时间(以秒为单位)
        for (Map.Entry<String, Integer> entry : cacheTimeConfig.entrySet()) {
            // 将当前缓存名称添加到cacheNames集合中
            cacheNames.add(entry.getKey());

            // 根据entry.getValue()(即缓存过期时间)设置defaultCacheConfig的过期时间,并将其放入cacheConfig映射中
            cacheConfig.put(entry.getKey(), defaultCacheConfig.entryTtl(Duration.ofSeconds(entry.getValue())));
        }

        // 使用RedisCacheManager的builder模式创建RedisCacheManager实例
        return RedisCacheManager.builder(redisCacheWriter)
                // 设置默认的缓存配置
                .cacheDefaults(defaultCacheConfig)
                // 设置初始缓存名称列表
                .initialCacheNames(cacheNames.build())
                // 设置每个缓存的特定配置
                .withInitialCacheConfigurations(cacheConfig.build())
                // 构建并返回最终的RedisCacheManager实例
                .build();
    }
}

如果我们需要给key配置失效时间,需要在yml中写入就好.如果希望key不失效,不写配置就好。

cachetime:
  config: "{customerItemlist:3600,test:1200}"

实现工具类

@Slf4j
@Configuration
public class SwitchUtils {


    /**
     * 海报文案
     */
    public static String Default_Poster_KEY = "DefaultUserShowCashKEY";
    public static Boolean Default_Poster_VAL = false;

    /**
     * 不可分享的优惠券id
     */
    public static String DefaultCanNotGive_KEY = "DefaultCanNotGive";
    public static String DefaultCanNotGive_VAL = "[10155,10165,10167]";

    public static Map<String, Object> switchMap = new HashMap<>();

    static {
        switchMap.put(Default_Poster_KEY, Default_Poster_VAL);
        switchMap.put(DefaultCanNotGive_KEY, DefaultCanNotGive_VAL);
    }

    /**
     * 单值类型
     */
    @Cacheable(cacheNames = "SWITCH", key = "#switchKey + '_' + #clazz.getSimpleName()", unless = "#result == null")
    public <T> T getSwitch(String switchKey, Class<T> clazz) {
        Object value = switchMap.get(switchKey);
        // 基础类型转换
        if (clazz.isAssignableFrom(Boolean.class)) {
            return clazz.cast(value);
        } else if (clazz.isAssignableFrom(String.class)) {
            return clazz.cast(value);
        } else if (clazz.isAssignableFrom(Long.class)) {
            return clazz.cast(value);
        } else if (clazz.isAssignableFrom(Double.class)) {
            return clazz.cast(value);
        }
        // 其他未知类型
        throw new IllegalArgumentException("Unsupported type: " + clazz.getName());
    }

    @Cacheable(cacheNames = "SWITCH", key = "#switchKey", unless = "#result == null")
    public <T> List<T> getStringListSwitch(String switchKey, Class<T> clazz) {
        String defaultValue = (String) switchMap.get(switchKey);
        if (Objects.isNull(defaultValue)) {
            return null;
        }
        return JSONObject.parseArray(defaultValue, clazz);
    }
}

测试

        List<Integer> stringListSwitch = switchUtils.getStringListSwitch(SwitchUtils.DefaultCanNotGive_KEY, Integer.class);
        System.out.println(stringListSwitch);
        Boolean val = switchUtils.getSwitch(SwitchUtils.Default_Poster_KEY, Boolean.class);
        System.out.println(val);
写一个简单的动态配置开关,第1张
1710579757887.png

后续设计

如果知识开发同事需要的话,这样的设计已经足够使用了,也可以把k,v存储在db或者配置文件中,如果需要运营同学使用,还需要提供更新缓存的接口。同时注意风控。


https://www.xamrdz.com/backend/39p1936502.html

相关文章: