[转]spring cloud config实现datasource的热部署

本文介绍如何利用 Spring Cloud Config 实现数据源的热部署,即在 GitLab 修改配置后,无需重启服务即可使新的数据源配置生效。文中详细展示了通过 @RefreshScope 和 @ConfigurationProperties 实现配置刷新的方法,并提供了自定义数据源热部署的具体代码。

转自:http://blog.youkuaiyun.com/liuchuanhong1/article/details/75446850

关于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个步骤,上面代码中已标注。

<think>好的,我现在需要帮用户深度理解Spring Cloud Alibaba。首先,用户可能已经对Spring Cloud有一定了解,但想进一步了解Alibaba的组件。我需要从基础概念入手,逐步深入。 首先,我应该先介绍Spring Cloud Alibaba是什么,它的定位和优势。比如,它是Spring Cloud的子项目,由阿里开源,提供微服务解决方案。然后,对比原生Spring Cloud,说明它的优势,比如更好的阿里生态集成,更丰富的功能模块。 接下来,用户可能需要了解核心组件。这时候要列举Nacos、Sentinel、Seata等,每个组件的作用和特点。比如Nacos作为注册配置中心,对比Eureka和Config;Sentinel做流量控制,对比Hystrix;Seata处理分布式事务。需要详细说明每个组件的功能和使用场景。 然后,用户可能想知道如何与Spring Cloud集成,这里需要给出一些例子,比如依赖配置,注解使用。例如,使用Nacos作为注册中心,如何在pom.xml中添加依赖,配置文件怎么写,启动类如何标注。这部分要具体,方便用户实践。 另外,用户可能关心实际应用中的最佳实践,比如服务发现、配置管理、限流降级、事务处理。可以分点说明每个场景下如何选择和使用对应的组件,结合代码示例或配置片段。 还要考虑用户可能遇到的问题,比如版本兼容性,常见错误,如何排查。这部分可能需要经验积累,比如Spring Cloud Alibaba与Spring Boot版本的对应关系,启动时的常见异常及解决方法。 最后,总结Spring Cloud Alibaba的优势,比如社区活跃、文档丰富、适合国内场景,以及未来发展的趋势,比如云原生支持、Service Mesh整合等。这样用户能有一个全面的认识,并知道进一步学习的方向。 需要确保语言口语化,避免使用太多技术术语,但又要准确。结构要清晰,分点说明,重点部分用例子或代码加强理解。同时检查LaTeX格式是否正确,数学公式或代码块是否符合要求。</think>要深度理解Spring Cloud Alibaba,需要从以下五个层次逐步拆解: ### 一、定位与生态位 Spring Cloud Alibaba是Spring Cloud规范的阿里实现版,提供: 1. 与阿里云服务无缝集成的能力(如OSS、SchedulerX) 2. 更适合中国开发者习惯的中间件(Nacos替代Eureka+Config) 3. 解决原生Spring Cloud未覆盖的场景(如分布式事务) 4. 与Dubbo生态的深度整合(RPC通信) ### 二、核心组件矩阵 | 组件 | 作用领域 | 对比对象 | 核心优势 | |---------------|-------------------|----------------|------------------------------| | Nacos | 服务发现/配置中心 | Eureka+Config | 动态配置推送、AP/CP模式切换 | | Sentinel | 流量治理 | Hystrix | 可视化控制台、多样流量控制规则 | | Seata | 分布式事务 | - | AT/TCC/XA模式自由选择 | | RocketMQ | 消息队列 | Kafka/RabbitMQ | 事务消息、顺序消息 | | Dubbo | RPC通信 | Feign | 高性能、协议扩展性强 | ### 三、典型场景实现原理 **1. Nacos配置热更新机制** ```java @RefreshScope // 关键注解 @RestController public class ConfigController { @Value("${custom.config}") private String dynamicConfig; // 值变化时自动注入 } ``` 通过长轮询(Long Polling)实现配置变更的秒级推送,对比Spring Cloud Config需要手动触发/refresh端点 **2. Sentinel熔断策略** - 滑动时间窗口算法:统计最近10秒QPS $$ QPS = \frac{\sum_{i=1}^{10} reqCount_i}{10} $$ - 熔断条件:当QPS >阈值且异常比例>50%,触发熔断 ### 四、架构设计最佳实践 1. **混合部署模式**: - 传统Spring Cloud服务与Dubbo服务共存 - 通过Nacos统一注册发现 2. **多环境配置策略**: ```yaml spring: profiles: active: @profiles.active@ cloud: nacos: config: namespace: ${NACOS_NAMESPACE_${profiles.active}} ``` 3. **灰度发布方案**: - 使用Nacos元数据标记版本 - Sentinel结合标签路由实现流量染色 ### 五、深度优化方向 1. **Nacos内核调优**: - 调整心跳周期(默认5秒→10秒) - 集群使用Raft协议时优化选举超时时间 2. **Sentinel规则持久化**: ```java // 自定义DataSource扩展 public class NacosDataSource implements ReadableDataSource<String, List<FlowRule>> { // 实现规则从Nacos读取 } ``` 3. **Seata性能优化**: - 使用Redis作为事务日志存储 - 调整全局锁重试次数(默认30次→10次) ### 六、常见陷阱规避 1. **版本兼容问题**: - Spring Boot 2.4.x需使用2021.0.x版本 - JDK 17需升级到Nacos 2.1.x 2. **配置覆盖顺序**: ```properties # 优先级:环境变量 > Nacos配置 > 本地配置 spring.cloud.config.override-none=true ``` 3. **Sentinel误熔断**: ```java // 需排除业务异常计入熔断统计 Tracer.traceEntry(ex, EntryType.OUT); ``` 理解Spring Cloud Alibaba的关键在于抓住其"云原生中间件集成平台"的定位,通过实际业务场景反推技术选型,例如:秒杀场景需组合使用Sentinel(限流)+Seata(库存事务)+RocketMQ(削峰填谷)。建议从官方提供的[Spring Cloud Alibaba Example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples)入手实践。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值