公司的工程基于dubbo,不少工程,配置写在文件中是一个很麻烦的问题。配置管理成为急需解决的问题。java
采用配置中心,配置写在数据库,便于集中管理。各工程在文件中的配置固定不变。mysql
一、公共模块config-common,一个普通的jar工程redis
<groupId>com.wss.lsl</groupId> <artifactId>config-common</artifactId> <version>0.0.1-SNAPSHOT</version>
数据库配置spring
create database config_center; use config_center; -- 配置表 CREATE TABLE app_config( CONFIG_ID VARCHAR(40) COLLATE utf8_unicode_ci NOT NULL COMMENT '主键', ENV_NAME VARCHAR(40) COLLATE utf8_unicode_ci NOT NULL COMMENT '环境名:dev/test/product等', PROJECT_NAME VARCHAR(100) COLLATE utf8_unicode_ci NOT NULL COMMENT '工程名', CONFIG_NAME VARCHAR(100) COLLATE utf8_unicode_ci NOT NULL COMMENT '配置名', CONFIG_VALUE VARCHAR(500) COLLATE utf8_unicode_ci NOT NULL COMMENT '配置值', CONFIG_SORT INT(11) DEFAULT 1 COMMENT '配置排序:属性能够引用前面的属性', CONFIG_DESC VARCHAR(200) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '配置描述', CREATE_TIME datetime NOT NULL COMMENT '建立时间', UPDATE_TIME datetime NOT NULL COMMENT '更新时间', CREATE_USER VARCHAR(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '建立人', UPDATE_USER VARCHAR(20) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '修改人', IS_VALID INT(11) DEFAULT '1' COMMENT '是否有效:1有效、0无效', PRIMARY KEY (`CONFIG_ID`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='配置表'; INSERT INTO app_config(CONFIG_ID, ENV_NAME, PROJECT_NAME, CONFIG_NAME, CONFIG_VALUE, CONFIG_DESC, CREATE_TIME, UPDATE_TIME, CREATE_USER, UPDATE_USER, IS_VALID) VALUES ('752af6b371ee11e7a1bf080058000001', 'dev', 'boot-server', 'server.port', '8080', 'http监听端口', now(), now(), 'wei.ss', 'wei.ss', 1), ('786ccb9771ee11e7a1bf080058000001', 'dev', 'common', 'server.session.timeout', '1800', '会话超时时间', now(), now(), 'wei.ss', 'wei.ss', 1);
maven依赖,为了减小依赖,采用jdbc从数据库加载配置。sql
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
CommonConfig代码数据库
import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.env.Environment; /** * 公共配置 * * <pre> * 属性配置的优先级<br> FIXME * 优先级:操做系统环境变量 > application.properties > this project > common project * </pre> * * @author wei.ss * @date 2017年7月26日 */ @Configuration public class CommonConfig implements EnvironmentAware { private final Logger LOG = LoggerFactory.getLogger(getClass()); // @Autowired注入不进来,要实现接口EnvironmentAware才能够。 Environment env; @Override public void setEnvironment(Environment environment) { this.env = environment; } @Bean(name = "configProperties") public FactoryBean<Properties> configProperties() { String jdbcUrl = env.getProperty("config.center.jdbc.url"); String jdbcUser = env.getProperty("config.center.jdbc.user"); String jdbcPassword = env.getProperty("config.center.jdbc.password"); String envName = env.getProperty("config.center.env.name"); String projectName = env.getProperty("config.center.porject.name"); LOG.info( "工程的配置中心信息:jdbcUrl={},jdbcUser={},jdbcPassword={},envName={},projectName={}", jdbcUrl, jdbcUser, jdbcPassword, envName, projectName); return new PropertiesFactoryBean(jdbcUrl, jdbcUser, jdbcPassword, envName, projectName); } @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer( @Autowired @Qualifier("configProperties") FactoryBean<Properties> configProperties) throws Exception { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setProperties(configProperties.getObject()); configurer.setLocalOverride(false); // 确保从properties文件读取的配置优先级大于configProperties configurer.setTrimValues(true); // 去掉配置值先后的空格 return configurer; } }
PropertiesFactoryBean代码缓存
import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.FactoryBean; /** * 配置工厂bean * * @author wei.ss * @date 2017年7月26日 */ public class PropertiesFactoryBean implements FactoryBean<Properties> { private static final Logger LOG = LoggerFactory .getLogger(PropertiesFactoryBean.class); // 全部工程的公共配置 private static final String COMMON_PROJECT = "common"; // 查询配置的sql private static final String SQL = "SELECT CONFIG_NAME, CONFIG_VALUE, CONFIG_SORT FROM app_config " + "WHERE ENV_NAME='%1$s' AND PROJECT_NAME='%2$s' AND IS_VALID=1 ORDER BY CONFIG_SORT ASC"; private String jdbcUrl; private String jdbcUser; private String jdbcPassword; private String envName; private String projectName; private Properties properties; public PropertiesFactoryBean(String jdbcUrl, String jdbcUser, String jdbcPassword, String envName, String projectName) { super(); this.jdbcUrl = jdbcUrl; this.jdbcUser = jdbcUser; this.jdbcPassword = jdbcPassword; this.envName = envName; this.projectName = projectName; // 初始化工程配置 init(); } // 初始化工程配置 private void init() { LOG.info("开始初始化工程配置:envName={},projectName={}", envName, projectName); Connection connection = null; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); connection = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword); fillProperties(connection, COMMON_PROJECT); fillProperties(connection, projectName); } catch (Exception e) { LOG.error("初始化工程配置发生异常", e); LOG.error( "初始化工程发生异常参数:jdbcUrl={},jdbcUser={},jdbcPassword={},envName={}, projectName={}", jdbcUrl, jdbcUser, jdbcPassword, envName, projectName); throw new RuntimeException(e); } finally { LOG.info("初始化工程配置完成:properties={}", properties); if (null != connection) { try { connection.close(); } catch (SQLException e) { LOG.error("关闭数据库链接发生异常", e); } } } } // 填充属性配置 private void fillProperties(Connection connection, String projectName) { String sql = String.format(SQL, envName, projectName); Statement statement = null; ResultSet rs = null; try { statement = connection.createStatement(); rs = statement.executeQuery(sql); String configName = null; String configValue = null; while (rs.next()) { if (properties == null) { properties = new Properties(); } configName = rs.getString(1); configValue = rs.getString(2); properties.put(configName, configValue); } } catch (SQLException e) { LOG.error("查询工程配置发生异常", e); } finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { // ignore } } if (statement != null) { try { statement.close(); } catch (SQLException e) { // ignore } } } } @Override public Properties getObject() throws Exception { return properties; } @Override public Class<?> getObjectType() { return Properties.class; } @Override public boolean isSingleton() { return true; } }
二、其余工程引用配置session
引用config-commonapp
<dependency> <groupId>com.wss.lsl</groupId> <artifactId>config-common</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
加载配置maven
CommonConfig就是上面公共模块中的代码
@SpringBootApplication @Import({CommonConfig.class}) @PropertySource(value = { "file:/etc/pay_background/spring-boot/application.properties" }) public class BootApplication { // ... }
application.properties的内容以下:不一样环境只要修改config.center.jdbc.url和config.center.env.name,后期新增修改配置,只要往数据库添加或修改记录便可。
# 配置中心配置 config.center.jdbc.url=jdbc:mysql://192.168.1.104:3306/test?characterEncoding=utf-8 config.center.jdbc.user=root config.center.jdbc.password=123456 config.center.env.name=dev config.center.porject.name=boot-server
三、扩展优化。把配置写入数据库,新增一个工程config-server,专门从数据库加载配置写入缓存(如redis),其余工程从缓存读取配置,这样工程启动速度快,数据库的压力也小。