当项目达到一定程度,配置五花八门这时候配置中心的便派上了用场。
方案1 maven打包
如果只是要区分开发环境和上线环境的配置,只要打包的时候吧不同文件环境打包进来就ok了
maven举例
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<package.environment>local</package.environment>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<package.environment>dev</package.environment>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<package.environment>production</package.environment>
</properties>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<exclude>test/*</exclude>
<exclude>production/*</exclude>
<exclude>development/*</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources/${profiles.active}</directory>
</resource>
</resources>
</build>
或
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
<webResources>
<resource>
<directory>/config/${package.environment}/properties</directory>
<targetPath>WEB-INF/classes/properties</targetPath>
<filtering>false</filtering>
</resource>
</webResources>
</configuration>
</plugin>
mvn package –P dev
方案2 spring profiles
spring为beans标签提供了profile功能,以便项目的开发和生成环境分离。
参考:https://my.oschina.net/yybear/blog/113755
spring boot 不再赘述http://blog.csdn.net/he90227/article/details/52981747
方案3 Spring Cloud Config Server
maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.BUILD-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
服务端
@EnableConfigServer
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilderconfigure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class,args);
}
}
配置文件application.properties
(git)
spring.application.name=config-server
server.port=7001
spring.cloud.config.server.git.uri=https://github.com/www1350/springclouddemo
##搜索目录
spring.cloud.config.server.git.searchPaths=properties
spring.cloud.config.server.git.username=aaaa
spring.cloud.config.server.git.password=bbb
spring.cloud.config.enabled=true
(svn)
spring.application.name=config-server
server.port=7001
spring.cloud.config.server.svn.uri=svn://IP:port/project/config
spring.cloud.config.server.svn.username=absurd
spring.cloud.config.enabled=true
spring.cloud.config.server.svn.password=fdsaf
feign-consumer-dev.properties
userid=7
username=123
password=123
- /{application}/{profile}[/{label}]
- [/{label}/]{application}-{profile}.yml
- [/{label}/]{application}-{profile}.properties
http://localhost:7001/feign-consumer/dev/master
或
http://localhost:7001/master/feign-consumer-dev.yml
或
http://localhost:7001/master/feign-consumer-dev.properties
客户端调用(bootstrap.properties)
spring.application.name=feign-consumer
spring.cloud.config.profile=dev
spring.cloud.config.label=master
spring.cloud.config.uri=http://localhost:7001/
@RestController
@RefreshScope
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@Value("${userid}")
private Long userid;
@Value("${username}")
private String username;
@Value("${password}")
private String password;
@RequestMapping(value="/username",method= RequestMethod.GET)
@ResponseBody
public String getUserName(){
return username;
}
@RequestMapping(value="/dev",method= RequestMethod.GET)
@ResponseBody
public String getUser(){
return userService.getUserByParam(userid);
}
@RequestMapping(value="/{id}",method= RequestMethod.GET)
@ResponseBody
public String getUser(@PathVariable("id") Long id){
return userService.getUser(id);
}
}
下面这种写法也是可以的。
@Autowired
private Environment environment;
另外,他还提供了很多方式来满足需求。比如,修改了配置后,可以
$curl -X POST http://localhost:7001/refresh
来刷新配置。
$curl -X POST http://localhost:7001/restart
项目地址:https://github.com/www1350/springclouddemo
官方:https://github.com/spring-cloud/spring-cloud-config
方案4 Disconf
- Distributed Configuration Management Platform(分布式配置管理平台)
- 专注于各种「分布式系统配置管理」的「通用组件」和「通用平台」, 提供统一的「配置管理服务」。
功能特点 - 支持配置(配置项+配置文件)的分布式化管理
- 配置发布统一化
- 配置发布、更新统一化:
- 同一个上线包 无须改动配置 即可在 多个环境中(RD/QA/PRODUCTION) 上线
- 配置存储在云端系统,用户统一管理 多个环境(RD/QA/PRODUCTION)、多个平台 的所有配置
- 配置更新自动化:用户在平台更新配置,使用该配置的系统会自动发现该情况,并应用新配置。特殊地,如果用户为此配置定义了回调函数类,则此函数类会被自动调用。
- 极简的使用方式(注解式编程 或 XML无代码侵入模式):我们追求的是极简的、用户编程体验良好的编程方式。目前支持两种开发模式:基于XML配置或者基于注解,即可完成复杂的配置分布式化。
- 低侵入性或无侵入性、强兼容性:
- 低侵入性:通过极少的注解式代码撰写,即可实现分布式配置。
- 无侵入性:通过XML简单配置,即可实现分布式配置。
- 强兼容性:为程序添加了分布式配置注解后,开启Disconf则使用分布式配置;若关闭Disconf则使用本地配置;若开启Disconf后disconf-web不能正常Work,则Disconf使用本地配置。
- 支持配置项多个项目共享,支持批量处理项目配置。
- 配置监控:平台提供自校验功能(进一步提高稳定性),可以定时校验应用系统的配置是否正确。
注:配置项是指某个类里的某个Field字段。
官方文档:http://disconf.readthedocs.io/zh_CN/latest/
disconf-web安装
安装依赖软件
- Mysql(Ver 14.12 Distrib 5.0.45, for unknown-linux-gnu (x86_64) using EditLine wrapper)
- Tomcat(apache-tomcat-7.0.50)
- Nginx(nginx/1.5.3)
- zookeeeper (zookeeper-3.3.0)
- Redis (2.4.5)
项目地址:https://github.com/knightliao/disconf
打开disconf-web文件夹
- jdbc-mysql.properties (数据库配置)
- redis-config.properties (Redis配置,主要用于web登录使用)
- zoo.properties (Zookeeper配置)
- application.properties (应用配置)
注意,记得执行将application-demo.properties复制成application.properties:
cp application-demo.properties application.properties
上线前的初始化工作
初始化数据库:
可以参考 sql/readme.md 来进行数据库的初始化。注意顺序执行
0-init_table.sql
1-init_data.sql
201512/20151225.sql
20160701/20160701.sql
里面默认有6个用户(请注意线上环境删除这些用户以避免潜在的安全问题)
name | pwd |
---|---|
admin | admin |
testUser1 | MhxzKhl9209 |
testUser2 | MhxzKhl167 |
testUser3 | MhxzKhl783 |
testUser4 | MhxzKhl8758 |
testUser5 | MhxzKhl112 |
如果想自己设置初始化的用户名信息,可以参考代码来自己生成用户:
src/main/java/com/baidu/disconf/web/tools/UserCreateTools.java
部署War
修改server.xml文件,在Host结点下设定Context:
<Context path="" docBase="/home/work/dsp/disconf-rd/war"></Context>
并设置端口为 8015
启动Tomcat,即可。
部署 前端
修改 nginx.conf
upstream disconf {
server 127.0.0.1:8015;
}
server {
listen 80;
server_name localhost;
access_log /home/work/var/logs/disconf/access.log;
error_log /home/work/var/logs/disconf/error.log;
location / {
root /home/work/dsp/disconf-rd/war/html;
if ($query_string) {
expires max;
}
}
location ~ ^/(api|export) {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://localhost;
}
}
关于host
这里的 host 设置成 localhost (可以自定义),但它 必须与 application.properties 里的domain一样。
然后浏览器的访问域名也是这个。
新建app
新建配置项
新增配置文件
以config1.properties举例:
uid=1
password=4
username=www2
客户端
引入
<dependency>
<groupId>com.baidu.disconf</groupId>
<artifactId>disconf-client</artifactId>
<version>2.6.36</version>
</dependency>
基于xml的分布式配置
第一步:撰写配置文件
<bean id="disconfMgrBean" class="com.baidu.disconf.client.DisconfMgrBean"
destroy-method="destroy">
<property name="scanPackage" value="com.absurd"/>
</bean>
<bean id="disconfMgrBean2" class="com.baidu.disconf.client.DisconfMgrBeanSecond"
init-method="init" destroy-method="destroy">
</bean>
<context:component-scan base-package="com.absurd"/>
<aop:aspectj-autoproxy proxy-target-class="true"/>
第二步:撰写disconf.properties
# 是否使用远程配置文件
# true(默认)会从远程获取配置 false则直接获取本地配置
enable.remote.conf=true
#
# 配置服务器的 HOST,用逗号分隔 127.0.0.1:8000,127.0.0.1:8000
#
conf_server_host=localhost
# 版本, 请采用 X_X_X_X 格式
version=1_0_0_0
# APP 请采用 产品线_服务名 格式
app=absurd-app
# 环境
env=rd
# debug
debug=true
# 忽略哪些分布式配置,用逗号分隔
ignore=
# 获取远程配置 重试次数,默认是3次
conf_server_url_retry_times=1
# 获取远程配置 重试时休眠时间,默认是5秒
conf_server_url_retry_sleep_seconds=1
第三步:撰写配置类
以config1.properties举例:
@Service
@Scope("singleton")
@DisconfFile(filename = "config1.properties")
public class OneConfig {
private Long uid;
private String username;
private String password;
@DisconfFileItem(name = "uid", associateField = "uid")
public Long getUid() {
return uid;
}
public void setUid(Long uid) {
this.uid = uid;
}
@DisconfFileItem(name = "username", associateField = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@DisconfFileItem(name = "password", associateField = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
上面这种写法是托管配置,通过简单的注解类方式 托管配置。托管后,本地不需要此配置文件,统一从配置中心服务获取。
当配置被更新后,注解类的数据通过@Service
实现自动同步。
** 变量分布式配置 **
@Service
@Scope("singleton")
public class OneKeyConfig {
private Long key;
@DisconfItem(key = "absurd-app-rd-1")
public Long getKey() {
return key;
}
}
静态配置
@DisconfFile(filename = "config1.properties")
public class OneStaticConfig {
private static Long uid;
private static String username;
private static String password;
@DisconfFileItem(name = "uid", associateField = "uid")
public static Long getUid() {
return uid;
}
@DisconfFileItem(name = "username", associateField = "username")
public static String getUsername() {
return username;
}
@DisconfFileItem(name = "password", associateField = "password")
public static String getPassword() {
return password;
}
}
配置更新的通知
实现IDisconfUpdate 接口的reload方法,注意这里此类必须是JavaBean,Spring托管的,且 “scope” 都必须是singleton的。
添加 @DisconfUpdateService 注解,classes 值加上 OneConfig.class ,表示当 JedisConfig.class 这个配置文件更新时,此回调类将会被调用。或者,使用 confFileKeys 也可以。
@Service
@Scope("singleton")
@DisconfFile(filename = "config1.properties")
@DisconfUpdateService(classes = {OneConfig.class})
public class OneConfig implements IDisconfUpdate {
Logger logger = LoggerFactory.getLogger(getClass());
private Long uid;
private String username;
private String password;
@DisconfFileItem(name = "uid", associateField = "uid")
public Long getUid() {
return uid;
}
public void setUid(Long uid) {
this.uid = uid;
}
@DisconfFileItem(name = "username", associateField = "username")
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@DisconfFileItem(name = "password", associateField = "password")
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void reload() throws Exception {
logger.info(">>>>>>>>>>配置已改变>>>>>>>>>>>>");
}
}
当然也可以单独抽出来写一个类
基于xml配置
- 可以自动reload
<bean id="configproperties_disconf" class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:config2.properties</value>
</list>
</property>
</bean>
<bean id="propertyConfigurer" class="com.baidu.disconf.client.addons.properties.ReloadingPropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="propertiesArray" >
<list>
<ref bean="configproperties_disconf"/>
</list>
</property>
</bean>
如果有 <context:property-placeholder location="classpath*:properties/*.properties"/>
必须去掉否则会报错
- 不可以自动reload
<!-- 使用托管方式的disconf配置(无代码侵入, 配置更改不会自动reload)-->
<bean id="configproperties_no_reloadable_disconf"
class="com.baidu.disconf.client.addons.properties.ReloadablePropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath:config2.properties</value>
</list>
</property>
</bean>
<bean id="propertyConfigurerForProject1"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="propertiesArray">
<list>
<ref bean="configproperties_no_reloadable_disconf"/>
</list>
</property>
</bean>
过滤要进行托管的配置
忽略哪些分布式配置,用逗号分隔
ignore=jdbc-mysql.properties