Spring Cloud学习笔记3——配置中心

配置中心的配置内容可以来自自己的配置文件、数据库、Git及Subversion,本文只描述前面3种的用法。

例子码云地址:

配置中心:https://gitee.com/wudiyong/ConfigServer.git

其它客户端:https://gitee.com/wudiyong/userInfoService.git

配置内容来自配置文件、数据库

1、pom.xml文件配置

首先配置中心是一个Eureka客户端,所以要加入Eureka依赖:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>

还要加入配置中心依赖:

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>
pom.xml文件全部内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com</groupId>
	<artifactId>ConfigServer</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>ConfigServer</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Camden.SR6</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
 		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>druid</artifactId>
		    <version>1.1.3</version>
		</dependency>

	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

注意:

1)如果不需要连接数据库,则把数据库有关的依赖删掉,否则启动会报错

2)如果需要用到数据库,还要加上数据库驱动依赖,如mysql可加上如下依赖:

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.30</version>
</dependency>
如果是oracle则要注意,因为oracle授权原因,其驱动不能通过maven引入,当然,你可以自己定义maven仓库,然后把其放到里面,最简单的方式是到网上下载然后通过build path方式引入,如果你安装了oracle数据库,可以在安装路径中找到该驱动包,名字为ojdbc6.jar。

2、application.properties文件配置

配置中心本身就是一个Eureka客户端,所以需要向Eureka服务器注册:

spring.application.name=config-server
server.port=8999
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
如果不需要连接数据库,则不用做其它配置了,如果要连接数据库,则加入如下配置:

#spring.cloud.config.server.configSource=properties
spring.cloud.config.server.configSource=database

###########以下是配置中心的数据库配置信息###########
config.oracle.db.jdbc_driverClassName=oracle.jdbc.OracleDriver
config.oracle.db.jdbc_url=jdbc:oracle:thin:@localhost:1522:db001
config.oracle.db.jdbc_user=db001data
config.oracle.db.jdbc_password=123456

spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.cloud.config.server.configSource是自定义的属性,用于配置通过哪种方式读取配置内容。这里采用spring的JdbcTemplate方式来访问数据库,如果要结合mybatis,请看spring boot结合mybatis文章。
下面随便写一些要被读取的配置内容:

##############服务userInfo-service的配置信息####################
userInfo-service.config-server.database.datasource=ds1,ds2

userInfo-service.config-server.database.ds1.jdbc_driverClassName=oracle.jdbc.OracleDriver
userInfo-service.config-server.database.ds1.jdbc_url=jdbc:oracle:thin:@xx.xx.xx.xx:1521:xxxx
userInfo-service.config-server.database.ds1.jdbc_user=username
userInfo-service.config-server.database.ds1.jdbc_password=password

userInfo-service.config-server.database.ds2.jdbc_driverClassName=oracle.jdbc.OracleDriver
userInfo-service.config-server.database.ds2.jdbc_url=jdbc:oracle:thin:@xx.xx.xx.xx:1521:xxxx
userInfo-service.config-server.database.ds2.jdbc_user=username
userInfo-service.config-server.database.ds2.jdbc_password=password

userInfo-service.config-server.others.property1=property1Value
userInfo-service.config-server.others.property2=property2Value
属性的名字随意,为了区分这些属性属于哪个服务,通常会以服务名开头,如userInfo-service,因为一个服务(非配置中心)启动时,会调配置中心的EnvironmentRepository的实现类的public Environment findOne(String application, String profile, String label)方法,所以,我们可以根据这三个来自调用方的属性来读取属于它的配置信息。
application的值来自调用方的spring.application.name属性

profile的值来自调用方的spring.cloud.config.profile

label的值来自调用方的spring.cloud.config.label


3、Java文件

1)入口类加上如下注解:

@EnableConfigServer
@SpringBootApplication
@EnableEurekaClient
2)实现EnvironmentRepository接口:

package com.ConfigServer.environment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import com.ConfigServer.service.ConfigRepositoryService;
public class EnvironmentRepositoryImpl implements EnvironmentRepository{
	/*
	 * 用于标识配置信息从哪里取,如database代表从数据库中取,properties代表从配置文件中取
	 * 为null代表从配置文件和数据库中取,该属性是自己定义的
	 */
	@Value("${spring.cloud.config.server.configSource}")
	private String configSource;
	@Autowired
	private ConfigRepositoryService configRepositoryService;
	/*
	 * 其它服务启动的时该方法会被调用,从而获得属于自己的配置信息
	 */
	@Override
	public Environment findOne(String application, String profile, String label) {
		switch (configSource) {
		case "database":
			return configRepositoryService.getEnvironmentFromDatabaseByMybatis(application, profile, label);
		case "properties":
			return configRepositoryService.getEnvironmentFromProperties(application, profile, label);
		default:
			// TODO 可以把数据库和配置文件并集
			return null;
		}
	}
}

3)编写配置类,加载自定义的EnvironmentRepository实现类

package com.ConfigServer.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.config.server.environment.EnvironmentRepository;
import org.springframework.cloud.config.server.environment.NativeEnvironmentRepository;
import org.springframework.cloud.config.server.environment.SearchPathLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.ConfigurableEnvironment;
import com.ConfigServer.environment.EnvironmentRepositoryImpl;
@Configuration
public class EnvironmentRepositoryConfiguration {
	@Autowired
	 private ConfigurableEnvironment environment;
	 @Bean
	 public SearchPathLocator searchPathLocator() {
	  return new NativeEnvironmentRepository(environment);
	 }

	 /*
	  * Primary与Order都是用来控制当有多个相同bean的实现时,选择哪一个。
	  * @Primary告诉容器该实现的优先级最高,作用与@Order(Ordered.HIGHEST_PRECEDENCE)相同,二选一即可
	  * 值得注意的是,如果Spring Cloud版本是Edgware.RELEASE,使用@Primary启动会报错,此时可以使用Order,
	  * 两者都不用似乎也可以
	  */
	 @Order(Ordered.HIGHEST_PRECEDENCE)
//	 @Primary
	 @Bean
	 public EnvironmentRepository openEnvironmentRepository() {
	  return new EnvironmentRepositoryImpl();
	 }
}

4)编写读取配置内容的类

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.cloud.config.environment.Environment;
import org.springframework.cloud.config.environment.PropertySource;
import org.springframework.context.EnvironmentAware;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Service;
import com.ConfigServer.entity.NameValuePair;

@Service
public class ConfigRepositoryService implements EnvironmentAware{
	private org.springframework.core.env.Environment env;
	
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	//也可以采用如下方式注入Environment,而不用实现EnvironmentAware
//	@Autowired
//	private ConfigurableEnvironment env;
	
	@Override
	public void setEnvironment(org.springframework.core.env.Environment environment) {
		env = environment;
	}
	public Environment getEnvironmentFromProperties(String application, String profile, String label){
		//如果属性个数很少且名字确定,也可以用@Value的方式注入
		RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env,application+".");
		Map<String, Object> propertyMap = propertyResolver.getSubProperties(null);
		PropertySource propertySource = new PropertySource("configServer", propertyMap);
		Environment environment = new Environment(application, profile, label, "1.0", null);
		environment.add(propertySource);
		return environment;
	}

	public Environment getEnvironmentFromDatabase(String application, String profile, String label) {
		ResultSetExtractor<List<NameValuePair>> rse = new ResultSetExtractor<List<NameValuePair>>() {
			@Override
			public List<NameValuePair> extractData(ResultSet rs) throws SQLException, DataAccessException {
				List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
				while (rs.next()) {
					NameValuePair nameValuePair = new NameValuePair();
					nameValuePair.setName(rs.getString("key"));
					nameValuePair.setValue(rs.getString("value"));
					nameValuePairs.add(nameValuePair);
				}
				return nameValuePairs;
			}
		};
		String sql = "select key , value from config_info where application=?";
		Object[] params = new Object[1];
		params[0] = application;
		List<NameValuePair> nameValuePairs = jdbcTemplate.query(sql, params, rse);
		if(nameValuePairs == null || nameValuePairs.size() == 0)
			return null;
		Map<String, Object> propertyMap = new HashMap<String, Object>();
		for(NameValuePair nameValuePair:nameValuePairs) {
			propertyMap.put(nameValuePair.getName(), nameValuePair.getValue());
		}
		PropertySource propertySource = new PropertySource("configServer", propertyMap);
		Environment environment = new Environment(application, profile, label, "1.0", null);
		environment.add(propertySource);
		return environment;
	}
}

public class NameValuePair {
	private String name;
	private String value;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

5)如果连接数据库,则编写配置类,加载自定义DataSource(这里用的是com.alibaba.druid.pool.DruidDataSource连接池数据源)及JdbcTemplate:

import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import com.alibaba.druid.pool.DruidDataSource;

@Configuration
public class DruidDBConfig {
	private Logger log =  LoggerFactory.getLogger(DruidDBConfig.class);
	@Value("${config.oracle.db.jdbc_url}")
	private String dbUrl;
	@Value("${config.oracle.db.jdbc_user}")
	private String username;
	@Value("${config.oracle.db.jdbc_password}")
	private String password;
	@Value("${config.oracle.db.jdbc_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;

	@Bean
	@Primary
	public DataSource dataSource() {
		DruidDataSource datasource = new DruidDataSource();
		datasource.setUrl(this.dbUrl);
		datasource.setUsername(this.username);
		datasource.setPassword(this.password);
		datasource.setDriverClassName(this.driverClassName);
		// configuration
		datasource.setInitialSize(this.initialSize);
		datasource.setMinIdle(this.minIdle);
		datasource.setMaxActive(this.maxActive);
		datasource.setMaxWait(this.maxWait);
		datasource.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
		datasource.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
		datasource.setValidationQuery(this.validationQuery);
		datasource.setTestWhileIdle(this.testWhileIdle);
		datasource.setTestOnBorrow(this.testOnBorrow);
		datasource.setTestOnReturn(this.testOnReturn);
		datasource.setPoolPreparedStatements(this.poolPreparedStatements);
		datasource.setMaxPoolPreparedStatementPerConnectionSize(this.maxPoolPreparedStatementPerConnectionSize);
		try {
			datasource.setFilters(this.filters);
		} catch (SQLException e) {
			log.error("druid configuration initialization filter", e);
		}
		datasource.setConnectionProperties(this.connectionProperties);
		return datasource;
	}
	
	@Bean(name = "jdbcTemplate")
	public JdbcTemplate jdbcTemplate( @Qualifier("dataSource") DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}
}

数据库表config_info内容如下:


至此,配置中心已搭建完成,下面是其它Eureka客户端通过配置中心获取配置信息:

1、pom.xml加上如下配置:

		<dependency>
		  <groupId>org.springframework.cloud</groupId>
		  <artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
2、在bootstrap.properties中加入如下配置:
#配置中心,需要放在bootstrap.properties中才能生效
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=config-server
spring.cloud.config.profile=dev
spring.cloud.config.label=master

config-server是配置中心的名字

3、完成前两步后,服务在启动的时候已经获取到了配置中心的配置内容了,通过配置中心方式获取的配置信息存放在Environment对象中,该方式与本地配置文件方式效果一样。

本地配置方式中,如:

aaa.bbb.ccc=abc

xxxx=${aaa.bbb.ccc}123

则读取xxxx的值为abc123

同样的,假如通过配置中心的方式获取到xxxx的值为${}123,也会被替换成abc123,总之,配置中心与本地配置文件是同样效果的。

所以,可以通过如下方式获取配置项:

1、在属性前面使用@Value("${xxxx.xx.xxx}")

2、通过Environment获取

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import com.userInfoService.entity.PropertyEntity;
@Service
public class EnvironmentService implements EnvironmentAware{
	private Environment environment;
	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}
	
	public List<PropertyEntity> getDBConfig() {
		RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment,"config-server.database.");
		String dsPrefixs = propertyResolver.getProperty("datasource");
		if(dsPrefixs == null || "".equals(dsPrefixs))
			return null;
		List<PropertyEntity> propertyList = new ArrayList<PropertyEntity>();
		for(String dsPrefix:dsPrefixs.split(",")){
			PropertyEntity propertyEntity = new PropertyEntity();
			propertyEntity.setPrefix(dsPrefix);
			Map<String, Object> propertyMap = propertyResolver.getSubProperties(dsPrefix+".");
			propertyEntity.setProperties(propertyMap);
			propertyList.add(propertyEntity);
		}
		return propertyList;
	}
	
	public PropertyEntity getOtherConfigs() {
		RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment,"config-server.");
		PropertyEntity propertyEntity = new PropertyEntity();
		Map<String, Object> propertyMap = propertyResolver.getSubProperties("others.");
		propertyEntity.setPrefix("others");
		propertyEntity.setProperties(propertyMap);
		return propertyEntity;
	}
}
public class PropertyEntity {
	private String prefix;
	private Map<String, Object> properties;
	public String getPrefix() {
		return prefix;
	}
	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}
	public Map<String, Object> getProperties() {
		return properties;
	}
	public void setProperties(Map<String, Object> properties) {
		this.properties = properties;
	}
}

由于只在其它服务启动的时候会调一次配置中心的findOne方法,所以即使配置内容发生改变了,也不会重新获取最新内容,如果是Git或Subversion方式获取配置内容,则可以在其它服务的pom.xml文件加上如下配置来实现动态刷新配置内容:

		<dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
但由于上面是通过数据库和本地配置文件的方式,所以只能自己手动实现定时从配置中心读取配置信息,采用@Scheduled,下面是除配置中心外的其它服务的实现:

1)在启动类前面加上@EnableScheduling开启定时任务功能

2)编写定时任务,定时调用配置中心提供的接口

@Component
public class ConfigScheduled {
    @Scheduled(fixedRate = 3000)//单位是毫秒
    public void timerRate() {
        //调用配置中心提供的获取配置信息接口
    }
}
如何调用(看spring cloud的服务调用,可通过feign或RestTemplate)及如何更新自己的配置信息(怎么更新自己的Environment)省略。

配置中心在Controller类中,提供rest接口,当被调用时,就实时查询数据库,获取最新配置信息,如果配置中心是通过配置文件的方式,如何在配置文件修改时自动更新到自己的Environment中?目前我想到的方法是定时以读文件的方式读取配置文件内容,然后更新到Environment中。


其它Spring Cloud版本

如:

Spring Cloud采用Dalston.SR5,Spring Boot采用1.5.10.RELEASE

Spring Cloud采用Edgware.SR3,Spring Boot采用1.5.10.RELEASE

测试发现,如果采用Dalston.SR5,jar包下载不下来,而Edgware.SR3版本且没有问题


配置内容来自Git及Subversion


后续补充。。。。


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值