Spring Cloud 學習(29) --- Spring Cloud Config(四) Spring Cloud 配置抖拴、高可用

本地參數(shù)覆蓋遠程參數(shù)

spring:
  cloud:
    config:
      allow-override: true
      override-none: true
      override-system-properties: false
  • allow-override:標識 override-system-properties 是否啟用,默認為 true腥椒,設(shè)置為 false 時阿宅,意味著禁用用戶的設(shè)置
  • override-none:當此項為 true,override-override 為 true笼蛛,外部的配置優(yōu)先級更低洒放,而且不能覆蓋任何存在的屬性源。默認為 false
  • override-system-properties:用來標識外部配置是否能夠覆蓋系統(tǒng)配置滨砍,默認為 true
@ConfigurationProperties("spring.cloud.config")
public class PropertySourceBootstrapProperties {
    private boolean overrideSystemProperties = true;
    private boolean allowOverride = true;
    private boolean overrideNone = false;

    public PropertySourceBootstrapProperties() {
    }

    // 省略 getter往湿、setter
}

客戶端功能擴展

客戶端自動刷新

源碼:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-config/spring-cloud-autoconfig

在有些應(yīng)用上妖异,不需要再服務(wù)端批量推送的時候,客戶端本身需要獲取變化參數(shù)的情況下领追,使用客戶端的自動刷新能完成此功能他膳。

config server 依然采用 spring-cloud-config-simple-server,基礎(chǔ)配置不變蔓腐,配置文件 repo 依然是 https://gitee.com/laiyy0728/config-repo

配置拉取矩乐、刷新二方庫

新建一個二方庫(spring-cloud-autoconfig-refresh),用于其他項目引入回论,以自動刷新配置(用于多個子項目使用同一個配置中心散罕,自動刷新)

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-client</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
</dependencies>

整個二方庫只有這一個類,作用是獲取定時刷新時間傀蓉,并刷新配置

@Configuration
@ConditionalOnClass(RefreshEndpoint.class)
@ConditionalOnProperty("spring.cloud.config.refreshInterval")
@AutoConfigureAfter(RefreshAutoConfiguration.class)
@EnableScheduling
public class SpringCloudAutoconfigRefreshApplication implements SchedulingConfigurer {

    private static final Logger LOGGER = LoggerFactory.getLogger(SpringCloudAutoconfigRefreshApplication.class);

    @Autowired
    public SpringCloudAutoconfigRefreshApplication(RefreshEndpoint refreshEndpoint) {
        this.refreshEndpoint = refreshEndpoint;
    }

    @Value("${spring.cloud.config.refreshInterval}")
    private long refreshInterval;

    private final RefreshEndpoint refreshEndpoint;


    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        final long interval = getRefreshIntervalilliseconds();
        LOGGER.info(">>>>>>>>>>>>>>>>>>>>>> 定時刷新延遲 {} 秒啟動欧漱,每 {} 毫秒刷新一次配置 <<<<<<<<<<<<<<<<", refreshInterval, interval);
        scheduledTaskRegistrar.addFixedDelayTask(new IntervalTask(refreshEndpoint::refresh, interval, interval));
    }

    /**
     * 返回毫秒級時間間隔
     */
    private long getRefreshIntervalilliseconds() {
        return refreshInterval * 1000;
    }

}

/resources/META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.laiyy.gitee.config.springcloudautoconfigrefresh.SpringCloudAutoconfigRefreshApplication

客戶端引入二方庫

創(chuàng)建客戶端項目(spring-cloud-autoconfig-client)

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-client</artifactId>
    </dependency>

    <!-- 將配置好的自刷刷新作為二方庫引入 -->
    <dependency>
        <groupId>com.laiyy.gitee.config</groupId>
        <artifactId>spring-cloud-autoconfig-refresh</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

bootstrap.yml

spring:
  cloud:
    config:
      uri: http://localhost:9090
      label: master
      name: config-simple
      profile: dev

application.yml

server:
  port: 9091
spring:
  application:
    name: spring-cloud-autoconfig-client
  cloud:
    config:
      refreshInterval: 10 # 延遲時間、定時刷新時間

其余配置與 spring-cloud-config-simple-client 一致

驗證

啟動項目葬燎,訪問 http://localhost:9090/get-config-info 误甚,正常返回信息。修改 config repo 配置文件谱净,等待 10 秒后窑邦,再次訪問,可見返回信息已經(jīng)變?yōu)樾薷暮笮畔ⅰ?br> 查看 client 控制臺壕探,可見定時刷新日志

Exposing 2 endpoint(s) beneath base path '/actuator'
>>>>>>>>>>>>>>>>>>>>>> 定時刷新延遲 10 秒啟動
No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
Tomcat started on port(s): 9091 (http) with context path ''
Started SpringCloudAutoconfigClientApplication in 4.361 seconds (JVM running for 5.089)
Initializing Spring DispatcherServlet 'dispatcherServlet'
Initializing Servlet 'dispatcherServlet'
Fetching config from server at : http://localhost:9090      ------------------ 第一次請求
Completed initialization in 7 ms
Located environment: name=config-simple, profiles=[dev], label=master, version=00324826262afd5178a648a469247f4fffea945e, state=null
Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$f67277ed] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Fetching config from server at : http://localhost:9090       ------------------ 定時刷新配置
Located environment: name=config-simple, profiles=[dev], label=master, version=00324826262afd5178a648a469247f4fffea945e, state=null
Located property source: CompositePropertySource {name='configService', propertySources=[MapPropertySource {name='configClient'}, MapPropertySource {name='https://gitee.com/laiyy0728/config-repo/config-simple/config-simple-dev.yml'}]}

... 省略其他多次刷新

客戶端回退

客戶端回退機制冈钦,可以在出現(xiàn)網(wǎng)絡(luò)中斷時、或者配置服務(wù)因維護而關(guān)閉時李请,使得客戶端可以正常使用瞧筛。當啟動回退時,客戶端適配器將配置“緩存”到計算機中导盅。要啟用回退功能较幌,只需要指定緩存存儲的位置即可。

源碼:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-config/spring-cloud-config-fallback

config server 依然采用 spring-cloud-config-simple-server白翻,基礎(chǔ)配置不變乍炉,配置文件 repo 依然是 https://gitee.com/laiyy0728/config-repo

二方庫

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-rsa</artifactId>
        <version>1.0.7.RELEASE</version>
    </dependency>
</dependencies>

config-client.properties:用于設(shè)置是否開啟配置

spring.cloud.config.enabled=false

/resources/META-INF/spring.factories

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
  com.laiyy.gitee.config.springcloudconfigfallbackautorefresh.ConfigServerBootStrap
// 用于拉取遠程配置文件,并保存到本地
@Order(0)
public class FallbackableConfigServerPropertySourceLocator extends ConfigServicePropertySourceLocator {

    private static final Logger LOGGER = LoggerFactory.getLogger(FallbackableConfigServerPropertySourceLocator.class);

    private boolean fallbackEnabled;

    private String fallbackLocation;

    @Autowired(required = false)
    private TextEncryptor textEncryptor;

    public FallbackableConfigServerPropertySourceLocator(ConfigClientProperties defaultProperties, String fallbackLocation) {
        super(defaultProperties);
        this.fallbackLocation = fallbackLocation;
        this.fallbackEnabled = !StringUtils.isEmpty(fallbackLocation);
    }

    @Override
    public PropertySource<?> locate(Environment environment){
        PropertySource<?> propertySource = super.locate(environment);
        if (fallbackEnabled && propertySource != null){
            storeLocally(propertySource);
        }
        return propertySource;
    }

    /**
     * 轉(zhuǎn)換配置文件
     */
    private void storeLocally(PropertySource propertySource){
        StringBuilder builder = new StringBuilder();
        CompositePropertySource source = (CompositePropertySource) propertySource;
        for (String propertyName : source.getPropertyNames()) {
            Object property = source.getProperty(propertyName);
            if (textEncryptor != null){
                property = "{cipher}" + textEncryptor.encrypt(String.valueOf(property));
            }
            builder.append(propertyName).append("=").append(property).append("\n");
        }
        LOGGER.info(">>>>>>>>>>>>>>>>> file content: {} <<<<<<<<<<<<<<<<<<<", builder);
        saveFile(builder.toString());
    }

    /**
     * 保存配置到本地
     * @param content 配置內(nèi)容
     */
    private void saveFile(String content){
        File file = new File(fallbackLocation + File.separator + ConfigServerBootStrap.FALLBACK_NAME);
        try {
            FileCopyUtils.copy(content.getBytes(), file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


// 用于判斷從遠程拉取配置文件,還是從本地拉瓤獬怠(spring boot 2.0,spring cloud F版)
@Configuration
@EnableConfigurationProperties
@PropertySource(value = {"config-client.properties","file:{spring.cloud.config.fallback-location:}/fallback.properties"}, ignoreResourceNotFound = true)
public class ConfigServerBootStrap {

    public static final String FALLBACK_NAME = "fallback.properties";

    private final ConfigurableEnvironment configurableEnvironment;

    @Autowired
    public ConfigServerBootStrap(ConfigurableEnvironment configurableEnvironment) {
        this.configurableEnvironment = configurableEnvironment;
    }

    @Value("${spring.cloud.config.fallback-location:}")
    private String fallbackLocation;

    @Bean
    public ConfigClientProperties configClientProperties(){
        ConfigClientProperties configClientProperties = new ConfigClientProperties(this.configurableEnvironment);
        configClientProperties.setEnabled(false);
        return configClientProperties;
    }

    @Bean
    public FallbackableConfigServerPropertySourceLocator fallbackableConfigServerPropertySourceLocator(){
        ConfigClientProperties client = configClientProperties();
        return new FallbackableConfigServerPropertySourceLocator(client, fallbackLocation);
    }

}

在 SpringBoot 1.0、Spring Cloud G 版中面哥,會啟動報錯:

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'configClientProperties', defined in class path resource [com/laiyy/gitee/config/springcloudconfigfallbackautorefresh/ConfigServerBootStrap.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/cloud/config/client/ConfigServiceBootstrapConfiguration.class] and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

2019-03-07 10:10:11.230 ERROR 13828 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'configClientProperties' defined in class path resource [com/laiyy/gitee/config/springcloudconfigfallbackautorefresh/ConfigServerBootStrap.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.laiyy.gitee.config.springcloudconfigfallbackautorefresh.ConfigServerBootStrap; factoryMethodName=configClientProperties; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/laiyy/gitee/config/springcloudconfigfallbackautorefresh/ConfigServerBootStrap.class]] for bean 'configClientProperties': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration; factoryMethodName=configClientProperties; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/cloud/config/client/ConfigServiceBootstrapConfiguration.class]] bound.
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894) ~[spring-beans-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:274) ~[spring-context-5.1.2.RELEASE.jar:5.1.2.RELEASE]
    ....

如果按照報錯提示,增加了 spring.main.allow-bean-definition-overriding=true 的配置颊埃,沒有任何作用仁讨;如果修改了 bean 名稱

@Bean(name="clientProperties")
public ConfigClientProperties configClientProperties(){
    ConfigClientProperties configClientProperties = new ConfigClientProperties(this.configurableEnvironment);
    configClientProperties.setEnabled(false);
    return configClientProperties;
}

會有如下報錯:

***************************
APPLICATION FAILED TO START
***************************

Description:

Method configClientProperties in org.springframework.cloud.config.client.ConfigClientAutoConfiguration required a single bean, but 2 were found:
    - configClientProperties: defined by method 'configClientProperties' in class path resource [org/springframework/cloud/config/client/ConfigClientAutoConfiguration.class]
    - clientProperties: defined by method 'configClientProperties' in class path resource [com/laiyy/gitee/config/springcloudconfigfallbackautorefresh/ConfigServerBootStrap.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

原因:ConfigClientProperties 在初始化時已經(jīng)默認單例加載随珠。即:這個 bean 不能被重新注冊到 spring 容器中。
解決辦法:將 spring 容器已經(jīng)加載的單例的 ConfigClientProperties 注入進來猬错,并在構(gòu)造中設(shè)置為 false 即可

@Configuration
@EnableConfigurationProperties
@PropertySource(value = {"configClient.properties", "file:${spring.cloud.config.fallbackLocation:}/fallback.properties"}, ignoreResourceNotFound = true)
public class ConfigServerBootStrap {

    public static final String FALLBACK_NAME = "fallback.properties";

    private final ConfigurableEnvironment configurableEnvironment;

    private final ConfigClientProperties configClientProperties;

    @Autowired
    public ConfigServerBootStrap(ConfigurableEnvironment configurableEnvironment, ConfigClientProperties configClientProperties) {
        this.configurableEnvironment = configurableEnvironment;
        this.configClientProperties = configClientProperties;

        this.configClientProperties.setEnabled(false);
    }

    @Value("${spring.cloud.config.fallbackLocation:}")
    private String fallbackLocation;

    @Bean
    public FallbackableConfigServerPropertySourceLocator fallbackableConfigServerPropertySourceLocator() {
        return new FallbackableConfigServerPropertySourceLocator(configClientProperties, fallbackLocation);
    }
}

config client

bootstrap.yml

spring:
  cloud:
    config:
      uri: http://localhost:9090
      label: master
      name: config-simple
      profile: dev
      fallbackLocation: E:\\springcloud

application.yml

server:
  port: 9091
spring:
  application:
    name: spring-cloud-autoconfig-client
  main:
    allow-bean-definition-overriding: true
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always

其余配置窗看、JAVA 類不變

驗證

啟動 config client,查看控制臺倦炒,可見打印了 2 次遠程拉取同步本地文件的信息:

>>>>>>>>>>>>>>>>> file content: config.client.version=ee39bf20c492b27c2d1b1d0ff378ad721e79a758
com.laiyy.gitee.config=dev 環(huán)境显沈,git 版 spring cloud config-----!
 <<<<<<<<<<<<<<<<<<<

查看本地 E:\springcloud 文件夾,可見多了一個 fallback.properties 文件

fallback local properties

文件內(nèi)容:


fallback local properties content

更新 config repo 的對應(yīng)配置文件后逢唤,POST 訪問 config client 刷新端口:http://localhost:9091/actuator/refresh 可見控制臺再次打印同步本地文件信息拉讯。此時停止 config server 訪問,再次訪問 http://localhost:9091/get-config-info 鳖藕,返回的信息是同步后的更新結(jié)果魔慷,由此驗證客戶端回退成功。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末著恩,一起剝皮案震驚了整個濱河市院尔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喉誊,老刑警劉巖邀摆,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異伍茄,居然都是意外死亡栋盹,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門幻林,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贞盯,“玉大人,你說我怎么就攤上這事沪饺□锔遥” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵整葡,是天一觀的道長件余。 經(jīng)常有香客問我,道長遭居,這世上最難降的妖魔是什么啼器? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮俱萍,結(jié)果婚禮上端壳,老公的妹妹穿的比我還像新娘。我一直安慰自己枪蘑,他們只是感情好损谦,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布岖免。 她就那樣靜靜地躺著,像睡著了一般照捡。 火紅的嫁衣襯著肌膚如雪颅湘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天栗精,我揣著相機與錄音闯参,去河邊找鬼。 笑死悲立,一個胖子當著我的面吹牛鹿寨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播级历,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼释移,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了寥殖?” 一聲冷哼從身側(cè)響起玩讳,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嚼贡,沒想到半個月后熏纯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡粤策,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年樟澜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叮盘。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡秩贰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柔吼,到底是詐尸還是另有隱情毒费,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布愈魏,位于F島的核電站觅玻,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏培漏。R本人自食惡果不足惜溪厘,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望牌柄。 院中可真熱鬧畸悬,春花似錦、人聲如沸珊佣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至妆档,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間虫碉,已是汗流浹背贾惦。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留敦捧,地道東北人须板。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像兢卵,于是被迫代替她去往敵國和親习瑰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

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