配置中心指的是将传统的.propertis或.yaml以及项目中些临时需要修改的数据,通过一个配置中心来统一规范管理。
实时更新:线上某些业务需要立即启动,只需要修改下配置几秒钟就更新了;
高可用:有一套行之有效的保障方案;
配置与应用分离:解决了需要修改配置居然还要重启项目....;
统一管理统一标准:规范了各种DIY问题;
个人理解:比如你线上有500台服务器,现在要立刻将某个业务暂时或启动,要是传统要重新修改配置文件->再发版,这时候时间已过...但是有了配置中心,只需要修改一下配置,几秒钟后全部生效,马上开始进行。
阿里的ACM(收费)
应用配置管理ACM(Application Configuration Management)是一款在分布式架构环境中对应用配置进行集中管理和推送的产品。凭借配置变更、配置推送、历史版本管理、灰度发布、配置变更审计等配置管理工具,ACM能帮助您集中管理所有应用环境中的配置,降低分布式系统中管理配置的成本,并降低因错误的配置变更造成可用性下降甚至发生故障的风险。
https://help.aliyun.com/learn/learningpath/acm.html?spm=5176.163362.847321.learning.3d7c2539omBLESDiamond(淘宝钻石)
diamond是淘宝内部使用的一个管理持久配置的系统,它的特点是简单、可靠、易用,目前淘宝内部绝大多数系统的配置,由diamond来进行统一管理。
https://github.com/takeseem/diamond(已经不维护)
https://github.com/gzllol/diamond
Apollo(阿波罗):
Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。
https://www.bookstack.cn/read/ctripcorp-apollo/66fd39d228fadcad.md
https://github.com/ctripcorp/apollo
Nacos(阿里巴巴):
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
https://nacos.io/zh-cn/docs/quick-start.html
https://juejin.im/entry/6844903846041387016
https://cloud.tencent.com/developer/article/1347918
http://blog.zollty.com/b/archive/config-center-selection.html
先拉下代码:https://github.com/gzllol/diamond
创建数据库权限和表
create database diamond;
grant all on diamond.* to CK@'%' identified by 'abc';
use diamond
-- 配置表
CREATE TABLE IF NOT EXISTS `config_info` (
`id` bigint(64) unsigned NOT NULL AUTO_INCREMENT,
`data_id` varchar(255) NOT NULL DEFAULT '',
`group_id` varchar(128) NOT NULL DEFAULT '',
`content` longtext NOT NULL,
`md5` varchar(32) NOT NULL DEFAULT '',
`src_ip` varchar(20) DEFAULT NULL,
`src_user` varchar(20) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT now(),
`gmt_modified` datetime NOT NULL DEFAULT now(),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_config_datagroup` (`data_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- 组表
CREATE TABLE IF NOT EXISTS `group_info` (
`id` bigint(64) unsigned NOT NULL AUTO_INCREMENT,
`address` varchar(70) NOT NULL DEFAULT '',
`data_id` varchar(255) NOT NULL DEFAULT '',
`group_id` varchar(128) NOT NULL DEFAULT '',
`src_ip` varchar(20) DEFAULT NULL,
`src_user` varchar(20) DEFAULT NULL,
`gmt_create` datetime NOT NULL DEFAULT now(),
`gmt_modified` datetime NOT NULL DEFAULT now(),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_group_address` (`address`,`data_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
登录diamond配置
添加配置
config: 123456
结果:发现这个diamond还是挺简单的。
接下来进行spring整合diamond。(发现idamond整合资料极少...)
注:先启动diamond-server
项目结构
diamond.properties
diamond.port=8090
diamond.config.ip=127.0.0.1
diamond.dataId=hong
com.hong.spring.config.diamond.ApplicationConfigurer
package com.hong.spring.config.diamond;
import com.taobao.diamond.manager.DiamondManager;
import com.taobao.diamond.manager.ManagerListener;
import com.taobao.diamond.manager.impl.DefaultDiamondManager;
import org.apache.commons.configuration.ConfigurationRuntimeException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.util.StringUtils;
import javax.naming.ConfigurationException;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.concurrent.Executor;
/**
*
* 功能描述: 动态拉取diamond!
*
* @param:
* @return:
* @auther: csh
* @date: 2020/11/9 11:34
*/
@Configuration
public class ApplicationConfigurer {
private static final Logger logger = LoggerFactory.getLogger(ApplicationConfigurer.class);
//diamond系统配置的dataId
String dataId="hong";
@Bean
public PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer() throws ConfigurationException, IOException {
PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
//加载diamond配置信息
loadDiamondConfig();
if (!StringUtils.isEmpty(dataId)) {
String[] dataIds = dataId.split(",");
for (int i = 0; i < dataIds.length; i++) {
//diamond客户端调用服务端获取配置信息
DiamondManager manager = new DefaultDiamondManager(dataIds[i], new ManagerListener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
//配置信息动态修改将触发此方法
System.out.println("receive config: " + configInfo);
propertyPlaceholderConfigurer.setProperties(getProperties(new StringReader(configInfo)));
}
});
/**
* 同步获取一份有效的配置信息,按照本地文件->diamond服务器->上一次正确配置的snapshot
* 的优先顺序获取, 如果这些途径都无效,则返回null
*
* @param timeout
* 从网络获取配置信息的超时,单位毫秒
* @return 配置信息
*/
String configInfo = manager.getAvailableConfigureInfomation(1000);
System.out.println("获取的内容:"+configInfo);
//关闭
//manager.close();
//解析Propertie配置信息
propertyPlaceholderConfigurer.setProperties(getProperties(new StringReader(configInfo)));
}
}
return propertyPlaceholderConfigurer;
}
/**
*
* 功能描述: 获取所有属性
*
* @param:
* @return:
* @auther: csh
* @date: 2020/11/9 11:34
*/
private Properties getProperties(StringReader in) {
Map map = getPropertisMap(in);
Properties properties = new Properties();
for (String key : map.keySet()) {
properties.setProperty(key, map.get(key));
}
return properties;
}
/**
*
* 功能描述: 解析文件内容
*
* @param:
* @return:
* @auther: csh
* @date: 2020/11/9 11:35
*/
private Map getPropertisMap(StringReader in) {
Map map = new HashMap();
PropertiesConfiguration.PropertiesReader reader = new PropertiesConfiguration.PropertiesReader(in);
try {
while (reader.nextProperty()) {
String key = reader.getPropertyName();
String value = reader.getPropertyValue();
map.put(key, value);
}
} catch (IOException ioex) {
throw new ConfigurationRuntimeException(ioex);
} finally {
try {
reader.close();
} catch (IOException e) {
logger.error("load config exception", e);
} finally {
return map;
}
}
}
/**
*
* 功能描述: 加载配置信息
*
* @param:
* @return:
* @auther: csh
* @date: 2020/11/9 11:35
*/
private void loadDiamondConfig() {
URL url = this.getClass().getClassLoader().getResource("diamond.properties");
if (url != null) {
File file = new File(url.getFile());
Properties prop = new Properties();
try {
prop.load(new FileInputStream(file));
dataId = prop.getProperty("diamond.dataId");
} catch (IOException e) {
}
}
}
}
修改diamond-client中的loadConfig方法:com.taobao.diamond.client.DiamondConfigure#loadConfig
注意这里的端口配成跟server一样的
URL url = this.getClass().getClassLoader().getResource("diamond.properties");
if (url != null) {
File file = new File(url.getFile());
Properties prop = new Properties();
try {
prop.load(new FileInputStream(file));
configServerAddress = prop.getProperty(Constants.CONF_KEY_CONFIG_IP, Constants.DEFAULT_DOMAINNAME);
String portStr = prop.getProperty(Constants.CONF_KEY_PORT, String.valueOf(port));
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException nfe) {
port = 8090;
}
} catch (IOException e) {
}
}
结果:
receive config: 777777777777777777777
receive config: 888888888888888888
https://blog.hufeifei.cn/2020/04/15/Alibaba/Diamond/
https://github.com/takeseem/diamond
https://my.oschina.net/piorcn/blog/340407
diamond总结:该组件算是国内第一个开源分布式配置中心,虽然说挺好用,但是文档极少并且在淘宝已不维护,所以不建议使用,维护成本挺高的!
apollo-configservice:提供配置获取接口,提供配置更新推送接口,接口服务对象为Apollo客户端
apollo-adminservice:提供配置管理接口,提供配置修改、发布等接口,接口服务对象为Portal,以及Eureka
apollo-portal:提供Web界面供用户管理配置
apollo-client:Apollo提供的客户端程序,为应用提供配置获取、实时更新等功能
Quick-Start启动
参考:https://github.com/ctripcorp/apollo/wiki/Quick-Start
先下载网盘链接下载,提取码: 9wwe
下载到本地后,在本地解压apollo-quick-start.zip
创建数据库:执行以下两份
登陆账号密码:apollo/admin
到这里可以发现对比diamond这个apollo所支持的功能比diamond多得多,但是复杂程度也是多好几个量级,如果非中大型项目不太建议,的确非常复杂,我们继续...
相关配置说明:
app.id:在配置中心配置的应用身份信息。
apollo.bootstrap.enabled:在应用启动阶段是否向Spring容器注入被托管的properties文件配置信息。
apollo.bootstrap.eagerLoad.enabled:将Apollo配置加载提到初始化日志系统之前。
apollo.bootstrap.namespaces:配置的命名空间,多个逗号分隔,一个namespace相当于一个配置文件。
**apollo.meta:**当前环境服务配置地址,生产环境建议至少双节点,可以填写多个逗号分隔,使用一个单独的域,如 http://config.xxx.com(由nginx等软件负载平衡器支持),而不是多个IP地址,因为服务器可能会扩展或缩小。
新建项目
新增属性
运行测试客户端:com.ctrip.framework.apollo.demo.api.SimpleApolloConfigDemo
运行配置如下(不配会找不到服务):
-Dapollo.configService=http://localhost:8080
结果:
动态加载,修改为999:
参考文档:https://github.com/ctripcorp/apollo/wiki/Apollo%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97
结果
18:40:41.371 [http-nio-8081-exec-4] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"999"}
修改为:123
结果
10:08:01.675 [http-nio-8081-exec-1] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"123"}
单个节点不算什么,再试试再开一个端口:8082
请求结果
10:11:54.076 [http-nio-8082-exec-6] INFO com.hong.spring.controller.IndexController - 获取配置信息{"val":"123"}
再试试修改为:123456789111
特别注意,每次修改后要发布:
结果
关于spring mvc整合完毕~
4.0.0
com.hong.springboot
springboot_all
0.0.1-SNAPSHOT
com.hong.springboot
springboot_apollo_client
0.0.1-SNAPSHOT
springboot_apollo_client
springboot??apollo
com.ctrip.framework.apollo
apollo-client
1.1.0
#id
app.id=hong
#环境 dev(开发)FAT()
env=DEV
#运行配置
#-Dapollo.meta=http://localhost:8080
apollo.meta=http://localhost:8080
apollo.bootstrap.enabled=true
#spring.application.name = reservation-service
#server.port = 8080
#logging.level = ERROR
#eureka.client.serviceUrl.defaultZone = http://127.0.0.1:8761/eureka/
#eureka.client.healthcheck.enabled = true
#eureka.client.registerWithEureka = true
#eureka.client.fetchRegistry = true
#eureka.client.eurekaServiceUrlPollIntervalSeconds = 60
#eureka.instance.preferIpAddress = true
# will inject 'application' namespace in bootstrap phase
apollo.bootstrap.enabled = true
# put apollo initialization before logging system initialization
apollo.bootstrap.eagerLoad.enabled=true
#端口
server.port=8083
/**
* @author: csh
* @Date: 2020/11/21 11:37
* @Description:
*/
@SpringBootApplication(scanBasePackages = "com.hong.springboot")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}