寫一個簡單的動態(tài)配置開關(guān)

前言

當(dāng)我們用srping cloud config做配置中心的時(shí)候由于缺少動態(tài)修改配置的功能,所以無法做到即使的對一些代碼流程做控制,每次修改配置都需要重啟服務(wù)從git拉取新的配置,這樣非常不方便所以我們需要一個輕量級的配置開關(guān)峡蟋,需要能做到不重啟服務(wù)就可以動態(tài)的修改配置苞也。

設(shè)計(jì)

基于目前不是很復(fù)雜的場景,我的想法是設(shè)計(jì)一個utils页屠。各種初始化的配置,直接由靜態(tài)代碼塊初始化到一個map即可勺阐,首次調(diào)用時(shí)我們把配置從java內(nèi)存加載到redis卷中,之后的調(diào)用只要redis有值就從redis獲取,如果redis沒值就從靜態(tài)map中獲取渊抽。之后只需要修改redis里面的值就可以做到動態(tài)的修改配置了蟆豫。

編碼

實(shí)現(xiàn)@Cacheable注解

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

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

    @Bean
    //當(dāng)容器不存在RedisTemplate時(shí),自定義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
    //當(dāng)容器不存在StringRedisTemplate時(shí)懒闷,自定義StringRedisTemplate
    @ConditionalOnMissingBean(StringRedisTemplate.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 設(shè)置springCache的過期時(shí)間
     */
    @Bean
    // 定義一個方法十减,用于創(chuàng)建RedisCacheManager實(shí)例栈幸,參數(shù)為RedisConnectionFactory對象
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 創(chuàng)建一個GenericFastJsonRedisSerializer實(shí)例,用于序列化和反序列化緩存數(shù)據(jù)到JSON格式
        GenericFastJsonRedisSerializer jsonRedisSerializer = new GenericFastJsonRedisSerializer();

        // 創(chuàng)建一個RedisSerializationContext.SerializationPair對象帮辟,指定使用jsonRedisSerializer作為值的序列化器
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer);

        // 獲取RedisCacheConfiguration的默認(rèn)配置速址,并進(jìn)行設(shè)置
        RedisCacheConfiguration defaultCacheConfig =
                RedisCacheConfiguration.defaultCacheConfig()
                        // 禁止緩存空值
                        .disableCachingNullValues()
                        // 設(shè)置緩存值的序列化方式為之前定義的pair
                        .serializeValuesWith(pair);

        // 創(chuàng)建一個非鎖定Redis緩存寫入器,與給定的RedisConnectionFactory關(guān)聯(lián)
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);

        // 使用ImmutableSet.Builder構(gòu)建器初始化一個不可變集合由驹,用于存儲自定義緩存名稱
        ImmutableSet.Builder<String> cacheNames = ImmutableSet.builder();

        // 使用ImmutableMap.Builder構(gòu)建器初始化一個不可變映射芍锚,用于存儲每個緩存名稱對應(yīng)的配置
        ImmutableMap.Builder<String, RedisCacheConfiguration> cacheConfig = ImmutableMap.builder();

        // 遍歷從properties文件中注入的cacheTimeConfig,該map存儲了各個緩存名稱及其過期時(shí)間(以秒為單位)
        for (Map.Entry<String, Integer> entry : cacheTimeConfig.entrySet()) {
            // 將當(dāng)前緩存名稱添加到cacheNames集合中
            cacheNames.add(entry.getKey());

            // 根據(jù)entry.getValue()(即緩存過期時(shí)間)設(shè)置defaultCacheConfig的過期時(shí)間蔓榄,并將其放入cacheConfig映射中
            cacheConfig.put(entry.getKey(), defaultCacheConfig.entryTtl(Duration.ofSeconds(entry.getValue())));
        }

        // 使用RedisCacheManager的builder模式創(chuàng)建RedisCacheManager實(shí)例
        return RedisCacheManager.builder(redisCacheWriter)
                // 設(shè)置默認(rèn)的緩存配置
                .cacheDefaults(defaultCacheConfig)
                // 設(shè)置初始緩存名稱列表
                .initialCacheNames(cacheNames.build())
                // 設(shè)置每個緩存的特定配置
                .withInitialCacheConfigurations(cacheConfig.build())
                // 構(gòu)建并返回最終的RedisCacheManager實(shí)例
                .build();
    }
}

如果我們需要給key配置失效時(shí)間并炮,需要在yml中寫入就好.如果希望key不失效,不寫配置就好甥郑。

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

實(shí)現(xiàn)工具類

@Slf4j
@Configuration
public class SwitchUtils {


    /**
     * 海報(bào)文案
     */
    public static String Default_Poster_KEY = "DefaultUserShowCashKEY";
    public static Boolean Default_Poster_VAL = false;

    /**
     * 不可分享的優(yōu)惠券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);
        // 基礎(chǔ)類型轉(zhuǎn)換
        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);
1710579757887.png

后續(xù)設(shè)計(jì)

如果知識開發(fā)同事需要的話逃魄,這樣的設(shè)計(jì)已經(jīng)足夠使用了,也可以把k,v存儲在db或者配置文件中澜搅,如果需要運(yùn)營同學(xué)使用伍俘,還需要提供更新緩存的接口。同時(shí)注意風(fēng)控勉躺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末癌瘾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子赂蕴,更是在濱河造成了極大的恐慌柳弄,老刑警劉巖舶胀,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件概说,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚣伐,警方通過查閱死者的電腦和手機(jī)糖赔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轩端,“玉大人放典,你說我怎么就攤上這事』穑” “怎么了奋构?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拱层。 經(jīng)常有香客問我弥臼,道長,這世上最難降的妖魔是什么根灯? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任径缅,我火速辦了婚禮掺栅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘纳猪。我一直安慰自己氧卧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布氏堤。 她就那樣靜靜地躺著沙绝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鼠锈。 梳的紋絲不亂的頭發(fā)上宿饱,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音脚祟,去河邊找鬼谬以。 笑死,一個胖子當(dāng)著我的面吹牛由桌,可吹牛的內(nèi)容都是我干的为黎。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼行您,長吁一口氣:“原來是場噩夢啊……” “哼铭乾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起娃循,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤炕檩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后捌斧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笛质,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年捞蚂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妇押。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡姓迅,死狀恐怖敲霍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情丁存,我是刑警寧澤肩杈,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站解寝,受9級特大地震影響扩然,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜编丘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一与学、第九天 我趴在偏房一處隱蔽的房頂上張望彤悔。 院中可真熱鬧,春花似錦索守、人聲如沸晕窑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杨赤。三九已至,卻和暖如春截汪,著一層夾襖步出監(jiān)牢的瞬間疾牲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工衙解, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阳柔,地道東北人。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓蚓峦,卻偏偏與公主長得像舌剂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子暑椰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一 基礎(chǔ)篇 1.1 Java基礎(chǔ) 面向?qū)ο蟮奶卣鞒橄?將一類對象的共同特征總結(jié)出來構(gòu)建類的過程霍转。繼承:對已有類的一...
    essential_note閱讀 694評論 0 0
  • 數(shù)據(jù)結(jié)構(gòu)隊(duì)列集合鏈表、數(shù)組字典一汽、關(guān)聯(lián)數(shù)組棧樹二叉樹完全二叉樹平衡二叉樹二叉查找樹(BST)紅黑樹B-避消,B+,B*樹...
    jackcooper閱讀 3,282評論 1 50
  • 設(shè)計(jì)模式 一.六大設(shè)計(jì)原則 1.開閉原則:針對擴(kuò)展開放召夹,修改關(guān)閉岩喷; 2.里氏替換原則:任何父類出現(xiàn)的地方都可由其子...
    說好的蔚藍(lán)天空呢閱讀 545評論 0 0
  • 前言 文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin39232820...
    六脈神劍的程序人生閱讀 420評論 0 2
  • JVM 說一下 jvm 的主要組成部分戳鹅?及其作用均驶? JVM包括類加載子系統(tǒng)、堆枫虏、方法區(qū)、棧爬虱、本地方法棧隶债、程序計(jì)數(shù)器...
    文刀雨木同閱讀 430評論 0 1