详解spring cloud config实现datasource的热部署

 更新时间:2018年01月16日 11:04:06   作者:牛奋lch  
这篇文章主要介绍了详解spring cloud config实现datasource的热部署,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

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

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

1、在客户端配置数据源

@RefreshScope 
@Configuration// 配置数据源 
public class DataSourceConfigure { 
 
  @Bean 
  @RefreshScope// 刷新配置文件 
  @ConfigurationProperties(prefix="spring.datasource") // 数据源的自动配置的前缀 
  public DataSource dataSource(){ 
    return DataSourceBuilder.create().build(); 
  } 
} 

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

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

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

package com.chhliu.springcloud.config;  
import java.sql.SQLException; 
import javax.sql.DataSource; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.cloud.context.config.annotation.RefreshScope; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.Primary;  
import com.alibaba.druid.pool.DruidDataSource; 
import lombok.extern.slf4j.Slf4j; 
 
/** 
 * 
 * 描述:如果不使用代码手动初始化DataSource的话,监控界面的SQL监控会没有数据("是spring boot的bug???") 
 * @author chhliu 
 * 创建时间:2017年2月9日 下午7:33:08 
 * @version 1.2.0 
 */ 
@Slf4j 
@Configuration 
@RefreshScope 
public class DruidConfiguration { 
  @Value("${spring.datasource.url}") 
  private String dbUrl; 
  @Value("${spring.datasource.username}") 
  private String username; 
  @Value("${spring.datasource.password}") 
  private String password; 
  @Value("${spring.datasource.driverClassName}") 
  private String driverClassName; 
  @Value("${spring.datasource.initialSize}") 
  private int initialSize; 
  @Value("${spring.datasource.minIdle}") 
  private int minIdle; 
  @Value("${spring.datasource.maxActive}") 
  private int maxActive; 
  @Value("${spring.datasource.maxWait}") 
  private int maxWait; 
  @Value("${spring.datasource.timeBetweenEvictionRunsMillis}") 
  private int timeBetweenEvictionRunsMillis; 
  @Value("${spring.datasource.minEvictableIdleTimeMillis}") 
  private int minEvictableIdleTimeMillis; 
  @Value("${spring.datasource.validationQuery}") 
  private String validationQuery; 
  @Value("${spring.datasource.testWhileIdle}") 
  private boolean testWhileIdle; 
  @Value("${spring.datasource.testOnBorrow}") 
  private boolean testOnBorrow; 
  @Value("${spring.datasource.testOnReturn}") 
  private boolean testOnReturn; 
  @Value("${spring.datasource.poolPreparedStatements}") 
  private boolean poolPreparedStatements; 
  @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize}") 
  private int maxPoolPreparedStatementPerConnectionSize; 
  @Value("${spring.datasource.filters}") 
  private String filters; 
  @Value("${spring.datasource.connectionProperties}") 
  private String connectionProperties; 
  @Value("${spring.datasource.useGlobalDataSourceStat}") 
  private boolean useGlobalDataSourceStat; 
 
  @Bean   //声明其为Bean实例 
  @Primary //在同样的DataSource中,首先使用被标注的DataSource 
  @RefreshScope 
  public DataSource dataSource(){ 
    DruidDataSource datasource = new DruidDataSource(); 
    datasource.setUrl(this.dbUrl); 
    datasource.setUsername(username); 
    datasource.setPassword(password); 
    datasource.setDriverClassName(driverClassName); 
 
    //configuration 
    datasource.setInitialSize(initialSize); 
    datasource.setMinIdle(minIdle); 
    datasource.setMaxActive(maxActive); 
    datasource.setMaxWait(maxWait); 
    datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); 
    datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); 
    datasource.setValidationQuery(validationQuery); 
    datasource.setTestWhileIdle(testWhileIdle); 
    datasource.setTestOnBorrow(testOnBorrow); 
    datasource.setTestOnReturn(testOnReturn); 
    datasource.setPoolPreparedStatements(poolPreparedStatements); 
    datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); 
    datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat); 
    try { 
      datasource.setFilters(filters); 
    } catch (SQLException e) { 
      log.error("druid configuration initialization filter: "+ e); 
    } 
    datasource.setConnectionProperties(connectionProperties); 
    return datasource; 
  } 
} 

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

从前面的博客中,我们不难发现,要想实现动态刷新,关键点就在post refresh的请求上,那我们就从刷新配置文件开始。
当我们post刷新请求的时候,这个请求会被actuator模块拦截,这点从启动的日志文件中就可以看出

复制代码 代码如下:

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

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

@ConfigurationProperties(prefix = "endpoints.refresh", ignoreUnknownFields = false) 
@ManagedResource 
public class RefreshEndpoint extends AbstractEndpoint<Collection<String>> { 
   private ContextRefresher contextRefresher; 
   public RefreshEndpoint(ContextRefresher contextRefresher) { 
    super("refresh"); 
    this.contextRefresher = contextRefresher; 
  }  
  @ManagedOperation 
  public String[] refresh() { 
    Set<String> keys = contextRefresher.refresh(); 
    return keys.toArray(new String[keys.size()]); 
  } 
   @Override 
  public Collection<String> invoke() { 
    return Arrays.asList(refresh()); 
  }  
} 

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

private RefreshScope scope; 
   public ContextRefresher(ConfigurableApplicationContext context, RefreshScope scope) { 
    this.context = context; 
    this.scope = scope; 
  }  
  public synchronized Set<String> refresh() { 
    Map<String, Object> before = extract( 
        this.context.getEnvironment().getPropertySources());// 1、before,加载提取配置文件 
    addConfigFilesToEnvironment();// 2、将配置文件加载到环境中 
    Set<String> keys = changes(before, 
        extract(this.context.getEnvironment().getPropertySources())).keySet();// 3、替换原来环境变量中的值 
    this.context.publishEvent(new EnvironmentChangeEvent(keys));// 4、发布变更事件, 
    this.scope.refreshAll(); 
    return keys; 
  } 

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • java实现简单扑克牌游戏

    java实现简单扑克牌游戏

    这篇文章主要为大家详细介绍了java实现简单扑克牌游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-09-09
  • java中关于转义字符的一个bug

    java中关于转义字符的一个bug

    本文主要介绍了java中关于转义字符的一个bug。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • 详细分析Java并发集合LinkedBlockingQueue的用法

    详细分析Java并发集合LinkedBlockingQueue的用法

    这篇文章主要介绍了详细分析Java并发集合LinkedBlockingQueue的用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • Java中String类(字符串操作)的10个常见问题和解决方法

    Java中String类(字符串操作)的10个常见问题和解决方法

    这篇文章主要介绍了Java中String类(字符串)操作的10个常见问题,需要的朋友可以参考下
    2014-04-04
  • Linux下Java开发环境搭建以及第一个HelloWorld

    Linux下Java开发环境搭建以及第一个HelloWorld

    这篇文章主要介绍了Linux下Java开发环境搭建以及第一个HelloWorld的实现过程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2015-09-09
  • JAVA Future类的使用详解

    JAVA Future类的使用详解

    这篇文章主要介绍了JAVA Future类的使用详解,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下
    2021-04-04
  • Java 超详细讲解ThreadLocal类的使用

    Java 超详细讲解ThreadLocal类的使用

    写SpringBoot项目的时候,经常用到的一个保存用户信息的类就是Threadlocal,我们今天就来详细介绍一下这个类,感兴趣的朋友来看看吧
    2022-04-04
  • SpringBoot整合atomikos实现跨库事务的详细方案

    SpringBoot整合atomikos实现跨库事务的详细方案

    这篇文章主要介绍了SpringBoot整合atomikos实现跨库事务,业务主要涉及政府及企业且并发量不大,所以采用XA事务,虽然性能有所损失,但是可以保证数据的强一致性,需要的朋友可以参考下
    2022-06-06
  • Java中包装类介绍与其注意事项

    Java中包装类介绍与其注意事项

    Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,所以在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类,有些地方也翻译为外覆类或数据类型类。
    2017-02-02
  • 简述springboot及springboot cloud环境搭建

    简述springboot及springboot cloud环境搭建

    这篇文章主要介绍了简述springboot及springboot cloud环境搭建的方法,包括spring boot 基础应用环境搭建,需要的朋友可以参考下
    2017-07-07

最新评论