spring cloud config实现datasource的热部署

关于spring cloud config的基本使用,前面的博客中已经说过了,如果不了解的话,请先看以前的博客

spring cloud config整合gitlab搭建分布式的配置中心

spring cloud config分布式配置中心的高可用

今天,我们的重点是如何实现数据源的热部署。

1、在客户端配置数据源

 

[java]  view plain  copy
 
  1. @RefreshScope  
  2. @Configuration// 配置数据源  
  3. public class DataSourceConfigure {  
  4.   
  5.     @Bean  
  6.     @RefreshScope// 刷新配置文件  
  7.     @ConfigurationProperties(prefix="spring.datasource"// 数据源的自动配置的前缀  
  8.     public DataSource dataSource(){  
  9.         return DataSourceBuilder.create().build();  
  10.     }  
  11. }  


通过上面的几个步骤,就可以实现在gitlab上修改配置文件,刷新后,服务器不用重启,新的数据源就会生效。

 

2、自定义数据源的热部署

当我们使用spring boot集成druid,我们需要手动来配置数据源,代码如下:

 

[java]  view plain  copy
 
  1. package com.chhliu.springcloud.config;  
  2.   
  3. import java.sql.SQLException;  
  4.   
  5. import javax.sql.DataSource;  
  6.   
  7. import org.springframework.beans.factory.annotation.Value;  
  8. import org.springframework.cloud.context.config.annotation.RefreshScope;  
  9. import org.springframework.context.annotation.Bean;  
  10. import org.springframework.context.annotation.Configuration;  
  11. import org.springframework.context.annotation.Primary;  
  12.   
  13. import com.alibaba.druid.pool.DruidDataSource;  
  14.   
  15. import lombok.extern.slf4j.Slf4j;  
  16.   
  17. /** 
  18.  *  
  19.  * 描述:如果不使用代码手动初始化DataSource的话,监控界面的SQL监控会没有数据("是spring boot的bug???") 
  20.  * @author chhliu 
  21.  * 创建时间:2017年2月9日 下午7:33:08 
  22.  * @version 1.2.0 
  23.  */  
  24. @Slf4j  
  25. @Configuration  
  26. @RefreshScope  
  27. public class DruidConfiguration {  
  28.     @Value("${spring.datasource.url}")  
  29.     private String dbUrl;  
  30.     @Value("${spring.datasource.username}")  
  31.     private String username;  
  32.     @Value("${spring.datasource.password}")  
  33.     private String password;  
  34.     @Value("${spring.datasource.driverClassName}")  
  35.     private String driverClassName;  
  36.     @Value("${spring.datasource.initialSize}")  
  37.     private int initialSize;  
  38.     @Value("${spring.datasource.minIdle}")  
  39.     private int minIdle;  
  40.     @Value("${spring.datasource.maxActive}")  
  41.     private int maxActive;  
  42.     @Value("${spring.datasource.maxWait}")  
  43.     private int maxWait;  
  44.     @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")  
  45.     private int timeBetweenEvictionRunsMillis;  
  46.     @Value("${spring.datasource.minEvictableIdleTimeMillis}")  
  47.     private int minEvictableIdleTimeMillis;  
  48.     @Value("${spring.datasource.validationQuery}")  
  49.     private String validationQuery;  
  50.     @Value("${spring.datasource.testWhileIdle}")  
  51.     private boolean testWhileIdle;  
  52.     @Value("${spring.datasource.testOnBorrow}")  
  53.     private boolean testOnBorrow;  
  54.     @Value("${spring.datasource.testOnReturn}")  
  55.     private boolean testOnReturn;  
  56.     @Value("${spring.datasource.poolPreparedStatements}")  
  57.     private boolean poolPreparedStatements;  
  58.     @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}")  
  59.     private int maxPoolPreparedStatementPerConnectionSize;  
  60.     @Value("${spring.datasource.filters}")  
  61.     private String filters;  
  62.     @Value("${spring.datasource.connectionProperties}")  
  63.     private String connectionProperties;  
  64.     @Value("${spring.datasource.useGlobalDataSourceStat}")  
  65.     private boolean useGlobalDataSourceStat;  
  66.   
  67.     @Bean     //声明其为Bean实例  
  68.     @Primary  //在同样的DataSource中,首先使用被标注的DataSource  
  69.     @RefreshScope  
  70.     public DataSource dataSource(){  
  71.         DruidDataSource datasource = new DruidDataSource();  
  72.         datasource.setUrl(this.dbUrl);  
  73.         datasource.setUsername(username);  
  74.         datasource.setPassword(password);  
  75.         datasource.setDriverClassName(driverClassName);  
  76.   
  77.         //configuration  
  78.         datasource.setInitialSize(initialSize);  
  79.         datasource.setMinIdle(minIdle);  
  80.         datasource.setMaxActive(maxActive);  
  81.         datasource.setMaxWait(maxWait);  
  82.         datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);  
  83.         datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);  
  84.         datasource.setValidationQuery(validationQuery);  
  85.         datasource.setTestWhileIdle(testWhileIdle);  
  86.         datasource.setTestOnBorrow(testOnBorrow);  
  87.         datasource.setTestOnReturn(testOnReturn);  
  88.         datasource.setPoolPreparedStatements(poolPreparedStatements);  
  89.         datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);  
  90.         datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);  
  91.         try {  
  92.             datasource.setFilters(filters);  
  93.         } catch (SQLException e) {  
  94.             log.error("druid configuration initialization filter: "+ e);  
  95.         }  
  96.         datasource.setConnectionProperties(connectionProperties);  
  97.         return datasource;  
  98.     }  
  99. }  


通过上面的示例,也可以实现数据源的动态刷新。接下来,我们就来看看,spring cloud config是怎么来实现数据源的热部署的。

 

从前面的博客中,我们不难发现,要想实现动态刷新,关键点就在post refresh的请求上,那我们就从刷新配置文件开始。

当我们post刷新请求的时候,这个请求会被actuator模块拦截,这点从启动的日志文件中就可以看出

 

[java]  view plain  copy
 
  1. Mapped "{[/refresh || /refresh.json],methods=[POST]}" onto public java.lang.Object org.springframework.cloud.endpoint.GenericPostableMvcEndpoint.invoke()  


接下来,我们就来看actuator定义的EndPoint,然后我们就找到了RefreshEndpoint这个类,该类的源码如下:

 

 

[java]  view plain  copy
 
  1. @ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false)  
  2. @ManagedResource  
  3. public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> {  
  4.   
  5.     private ContextRefresher contextRefresher;  
  6.   
  7.     public RefreshEndpoint(ContextRefresher contextRefresher) {  
  8.         super("refresh");  
  9.         this.contextRefresher = contextRefresher;  
  10.     }  
  11.   
  12.     @ManagedOperation  
  13.     public String[] refresh() {  
  14.         Set<String> keys = contextRefresher.refresh();  
  15.         return keys.toArray(new String[keys.size()]);  
  16.     }  
  17.   
  18.     @Override  
  19.     public Collection<String> invoke() {  
  20.         return Arrays.asList(refresh());  
  21.     }  
  22.   
  23. }  


从上面的源码,我们可以看到,重点在ContextRefresher这个类上,由于这个类太长了,下面把这个类的部分源码贴出来:

 

 

[java]  view plain  copy
 
  1. private RefreshScope scope;  
  2.   
  3.     public ContextRefresher(ConfigurableApplicationContext context, RefreshScope scope) {  
  4.         this.context = context;  
  5.         this.scope = scope;  
  6.     }  
  7.   
  8.     public synchronized Set<String> refresh() {  
  9.         Map<String, Object> before = extract(  
  10.                 this.context.getEnvironment().getPropertySources());// 1、before,加载提取配置文件  
  11.         addConfigFilesToEnvironment();// 2、将配置文件加载到环境中  
  12.         Set<String> keys = changes(before,  
  13.                 extract(this.context.getEnvironment().getPropertySources())).keySet();// 3、替换原来环境变量中的值  
  14.         this.context.publishEvent(new EnvironmentChangeEvent(keys));// 4、发布变更事件,  
  15.         this.scope.refreshAll();  
  16.         return keys;  
  17.     }  


从上面的代码不难看出,重点经历了4个步骤,上面代码中已标注。

http://blog.csdn.net/liuchuanhong1/article/details/75446850

猜你喜欢

转载自m635674608.iteye.com/blog/2400393