欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-10 14:24:34
关于spring cloud config的基本使用,前面的博客中已经说过了,如果不了解的话,请先看以前的博客 spring cloud config整合gitlab...

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

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