详解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序列化原理详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下
    2022-06-06
  • 通过FeignClient调用微服务提供的分页对象IPage报错的解决

    通过FeignClient调用微服务提供的分页对象IPage报错的解决

    这篇文章主要介绍了通过FeignClient调用微服务提供的分页对象IPage报错的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java自动装箱拆箱深入剖析

    java自动装箱拆箱深入剖析

    基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。java语言规范中说道:在许多情况下包装与解包装是由编译器自行完成的(在这种情况下包装成为装箱,解包装称为拆箱)
    2012-11-11
  • Mybatis学习笔记之动态SQL揭秘

    Mybatis学习笔记之动态SQL揭秘

    这篇文章主要给大家介绍了关于Mybatis学习笔记之动态SQL的相关资料,小编觉得挺不错的,对大家学习或者使用Mybatis会有一定的帮助,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • jpa EntityManager 复杂查询实例

    jpa EntityManager 复杂查询实例

    这篇文章主要介绍了jpa EntityManager 复杂查询实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 从JVM的内存管理角度分析Java的GC垃圾回收机制

    从JVM的内存管理角度分析Java的GC垃圾回收机制

    这篇文章主要介绍了从JVM的内存管理角度分析Java的GC垃圾回收机制,带有GC是Java语言的重要特性之一,需要的朋友可以参考下
    2015-11-11
  • Java  队列实现原理及简单实现代码

    Java 队列实现原理及简单实现代码

    这篇文章主要介绍了Java 队列实现原理及简单实现代码的相关资料,需要的朋友可以参考下
    2016-10-10
  • Java实现酒店客房管理系统

    Java实现酒店客房管理系统

    这篇文章主要为大家详细介绍了Java实现酒店客房管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • Spring Security在标准登录表单中添加一个额外的字段

    Spring Security在标准登录表单中添加一个额外的字段

    这篇文章主要介绍了Spring Security在标准登录表单中添加一个额外的字段,我们将重点关注两种不同的方法,以展示框架的多功能性以及我们可以使用它的灵活方式。 需要的朋友可以参考下
    2019-05-05
  • 基于Java实现收发电子邮件功能

    基于Java实现收发电子邮件功能

    Email就是电子邮件,我们平常使用的QQ邮箱,网易邮箱,Foxmail都是用来收发邮件的,利用Java程序也可以完成收发电子邮件的功能,本文就来为大家详细讲讲实现步骤
    2022-07-07

最新评论