统一配置中心2

*:first-child {
margin-top: 0 !important;
}

body>*:last-child {
margin-bottom: 0 !important;
}

/* BLOCKS
=============================================================================*/

p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}

/* HEADERS
=============================================================================*/

h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}

h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}

h1 {
font-size: 28px;
color: #000;
}

h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}

h3 {
font-size: 18px;
}

h4 {
font-size: 16px;
}

h5 {
font-size: 14px;
}

h6 {
color: #777;
font-size: 14px;
}

body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

统一配置中心方案

统一配置中心1中记录了我之前项目中如何处理多系统中的配置问题,对于统一配置中心组件一般分为两种做法:

自建

它的好外与缺点都非常明确。

好处

  • 设计以及代码实现都由自己把控,可形成自己的知识积累
  • 设计可以足够简化,无需考虑过多场景
  • 能够快速适应项目的需求,无需考虑开源的是否支持

缺点

  • 需要人力的投入,而且有重复造轮子的嫌疑,需要有足够的说服力
  • 对技术有一定要求,如果写出来的组件问题多或者通用性不强会形成浪费的局面
  • 缺少权威性,需要长时间的推进

开源

开源的配置中心,我之前有提到过百度的disconf,还有当当的config-toolkit,这些产品都有很多应用案例,功能也非常全。

好处

  • 权威性相对好,成功案例多的组件往往在技术推进上很比较容易
  • 不需要过多的人力投入,往往只需要一步一下按步骤来
  • 通用性比较强,适用的场景相对广泛

缺点

  • 可能并不完全与实际需求相符,需要通过一些扩展来支持
  • 可能会是重量级组件,而你的需求只使用其中一小部分
  • 可能存在BUG
  • 组件升级不受使用者控制

自建的方案

场景

当系统越来越多后,每个系统的配置如果没有统一的管理机制那么会非常难管理,需要有一种侵入性小的方案来将这些原本在单项目中维护的配置工作统一管理起来。

配置存储

推荐使用zookeeper来存储项目中的配置项,也可以是其它的存储。下面设计的配置中心组件是可扩展,但实现暂时只实现了zookeeper。

核心思路

在系统加载时,想办法将存储在zookeeper中的配置内容加载到系统变量中,然后程序就可以像调用本地配置文件一样了,不需要改变现有系统引用变量的行为。至于本地配置文件直接使用Spring自带功能即可,当然也可以统一在组件中。

实际需求

  • 配置存储在zookeeper
  • 支持本地配置
  • 有本地配置的情况下,以本地配置为准(本地调试)
  • 支持zookeeper热更新,即在运行时当zookeeper信息发生变更时能够实时同步到程序中

不需要支持本地文件的热更新

设计实现

时序图

内容有点多,先看下配置加载的时序图,不包含事件通知以及热更新。

启动组件入口

PropertyPlaceholderConfigurer,可以利用它来启动组件的初始化进而从zookeeper中加载数据到系统中,即先加载zookeeper数据然后加载本地配置文件。

@Bean
public static PropertyPlaceholderConfigurer properties() {
    SpringPropertyInjectSupport springPropertyInjectSupport=new SpringPropertyInjectSupport();
    springPropertyInjectSupport.setConfigNameSpaces("configcenter/"+System.getProperty("env"));
    springPropertyInjectSupport.init();
    PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
    Resource[] resources = new ClassPathResource[]
            { new ClassPathResource( "application.properties" ) };
    ppc.setLocations( resources );
    ppc.setIgnoreUnresolvablePlaceholders( true );
    return ppc;
}

SpringPropertyInjectSupport,是自定义的组件启动类,它负责加载zookeeper中的数据到系统:核心方法是init。

public void init() {
    if (this.configNameSpaces != null) {
        this.setSystemPropertiesFromConfigCenter();
    }
}

configNameSpaces是统一配置中心管理所有节点的父结点,比如configcenter/test是指测试环境的配置,系统中的配置节点名称不包含这些与框架相关的信息,只需要配置dataSource=XXX即代表configcenter/test/dataSource这个配置项。

ConfigCenterFactory是个单例用来返回统一配置中心实例,ConfigCenterService是配置接口,然后由配置接口获取所有的配置项(类型是一个Map),最后将Map中的信息导入到系统变量中。

private void setSystemPropertiesFromConfigCenter() {
    if (StringUtils.isBlank(this.configNameSpaces)) {
        return;
    }
    ConfigCenterFactory.getInstance().setSystemNameSpace(this.configNameSpaces);
    ConfigCenterService cc = ConfigCenterFactory.getInstance().getConfig(this.configNameSpaces);
    Map<String, Object> config = cc.getConfig();
    setSystemProperys(cc, config);

}

private void setSystemProperys(ConfigCenterService cc, Map<String, Object> config) {
    for (String key : config.keySet()) {
        String value = cc.get(key);
        if (key.contains(".")) {
            key = key.substring(1);
        }
        if (value == null) {
            value = "";
        }
        System.setProperty(key, value);
    }
}

ConfigCenterFactory

实例化配置组件,为了支持同时加载多个不同节点下的数据,所以以nameSpace做为key将实例放在Map中,比如想同时访问商品以及订单的配置,它们的namespace分别为

  • configcenter/order/test
  • configcenter/product/test
private static final Object lockObj = new Object();
private ConcurrentHashMap<String, ConfigCenterService> configCenterCache = null;
public ConfigCenterService getConfig(final String hosts, final String nameSpace) {

    Preconditions.checkNotNull(hosts);
    Preconditions.checkNotNull(nameSpace);

    StringBuilder sb = new StringBuilder(hosts);
    sb.append(nameSpace);

    final String key = sb.toString().intern();

    ConfigCenterService config = this.configCenterCache.get(key);
    if (config == null) {
        synchronized (lockObj) {
            if (!this.configCenterCache.containsKey(key)) {
                ConfigOption co = new ConfigOption(nameSpace, hosts);
                ConfigCenterService cc = new ConfigCenterServiceImpl(co);
                this.configCenterCache.put(key, cc);
            }
        }
    } else {
        return config;
    }

    return this.configCenterCache.get(key);
}

ConfigCenterService

配置管理接口,主要包含三部分内容:

  • 返回所有数据为一个Map
  • 数据更新通知,当某个配置变更后需要触发的功能。

不是更新系统中的变量,是指系统中的变量发生改变之后需要做什么?比如数据库的配置发生变化,此时需要重新刷新数据库连接池的信息等。
一些获取配置的协助类,即强类型化返回配置,减少调用端的类型转换。

public interface ConfigCenterService {
    public Map<String,Object> getConfig();
    public String get(String key);
    public Long getLongValue(String key);
    public Integer getIntegerValue(String key);
    public Double getDoubleValue(String key);
    public Boolean getBooleanValue(String key);
    public void notify(DataChangeEvent event);
    public void colse();
}

AbstractConfigCenterService

一些通用的逻辑实现放在这里。

ZkConfigCenterServiceImpl

是具体的配置接口实现类,继承AbstractConfigCenterService,实现ConfigCenterService接口。

  • zk客户端,选用curator-recipes,它很好的解决了重试等问题。
  • 连接zk
private void startClient() {
    if (this.client == null) {
        try {
            this.client = CuratorFrameworkFactory.builder()
                    .connectString(configOption.getZkUrls())
                    .namespace(configOption.getNameSpace())
                    .retryPolicy(configOption.getRetryPolicy())
                    .connectionTimeoutMs(20000)
                    .build();
            this.client.start();
            logger.info("zkclient start " + configOption.getNameSpace());

        } catch (Throwable e) {
            if(null!=this.client){
                this.client.close();
            }
            throw new RuntimeException("CuratorFrameworkFactory start error",e);
        }

    }
}
  • 加载配置
    从根目录开始一层一层住下加载,同时启用节点监控。
private void loadConfig(String nodePath) {

    try {
        if (StringUtils.isNotEmpty(nodePath)) {
            loadData(nodePath);
        }
        GetChildrenBuilder childrenBuilder = client.getChildren();

        try {
            List<String> children = null;
            if (StringUtils.isEmpty(nodePath)) {
                children = childrenBuilder.watched().forPath(null);
            } else {
                children = childrenBuilder.watched().forPath(nodePath);
            }

            loadChildsConfig(children, nodePath);
        } catch (Exception e) {
            throw Throwables.propagate(e);
        }

    } catch (Exception e) {
        logger.error("load zk config error namespace={}", configOption.getNameSpace());
        logger.error("load config error", e);
        this.client.close();
        throw new RuntimeException("load zk error");
    }
}
  • 监控配置变更
    当zookeeper的节点数据发生删除更新时会发起通知,我们根据通知信息来做相应的数据变更。
private Watcher getPathWatcher() {
    return new Watcher() {
        @Override
        public void process(WatchedEvent event) {
            if (event != null) {
                try {
                    boolean isDelete = false;
                    if (event.getState() == Event.KeeperState.SyncConnected) {

                        String path = event.getPath();
                        if (path == null || path.equals("/")) return;
                        switch (event.getType()) {
                            case NodeDeleted:
                                postRemovePath(event.getPath());
                                isDelete = true;
                                break;
                            case NodeDataChanged:
                                postDataChangeEvent(event);

                                break;
                            default:
                                break;
                        }

                        if (!isDelete) {
                            watchPathDataChange(event.getPath());
                        }
                    }

                } catch (Exception e) {
                    logger.info("zk data changed error:",e);
                }
            }
        }
    };
}

节点数据变更后的事件通知,采用guava的eventbus来实现。

private void postDataChangeEvent(WatchedEvent event) throws Exception {
    byte[] data = client.getData().forPath(event.getPath());
    String value = new String(data, Charsets.UTF_8);
    String key = event.getPath().replace("/", ".");
    postDataChangeKeyValue(key, value);
}

private void postDataChangeKeyValue(String key, String value) {
    this.config.put(key, value);

    Map<String, Object> map = new HashMap<String, Object>();
    map.put(key, value);
    DataChangeEvent dataChangeEvent = new DataChangeEvent(map);

    configOption.getEnventBus().post(dataChangeEvent);

}

源码

统一配置源码

源码参考了网上开源的项目,由于时间久远原项目已经找到地址了。
我公布的源码是经过我重新整理后的结果,并非全部出自于自己。

时间: 2024-08-25 01:15:58

统一配置中心2的相关文章

统一配置中心

之前我的2015下半年总结中有提到我们的项目采用了微服务的模式,也就是说系统按一定的技术以及业务切分成各个独立的小系统,比如我们的产品是一个电商系统,那么可以分为:前端WAP,前端api,商品管理系统,采购系统,主数据管理系统,用户中心管理,价格管理系统,促销管理系统,订单管理系统,库存管理系统,门店管理系统等等,最后统计的数据是dubbo服务就高达18个,web系统有3个,前端WAP站点一个.这些系统要想跑起来就需要连接各种资源,比如服务地址,数据库,缓存,文件系统,消息队列等,一般项目中使用

基于ZK构建统一配置中心的方案和实践

背景: 近期使用Zk实现了一个简单的配置管理的小东西,在此开源出来,有兴趣的希望提出您的宝贵意见.如果恰巧您也使用或者接触过类似的东西, 也希望您可以分享下您觉得现在这个项目可以优化和改进的地方. 项目地址: https://github.com/cncduLee/zk-ucc 需求: 1.不需要重启应用.修改代码就可以对业务做开关操作 2.避免不必要的重复拉取配置信息,降低网络IO,节省服务器资源. 例如1:开发一个评论系统,违禁词这个东西,应该是可配置的,同时又是变动极少的.如何设计这个违禁

spring cloud互联网分布式微服务云平台规划分析--spring cloud服务统一配置中心

1.介绍鸿鹄云架构[服务统一配置中心]为分布式系统中的外部配置提供服务器和客户端支持.使用commonservice-config,可以在所有环境中管理应用程序的外部属性.应用程序可通过从开发人员到测试和生产的部署流程,可以管理这些环境之间的配置,并确定应用程序具有迁移时需要运行的一切.服务器存储后端的默认实现使用git,因此它轻松支持标签版本的配置环境,以及可以访问用于管理内容的各种工具.很容易添加替代实现,并使用Spring Cloud Bus配置刷新方案.更多资源欢迎球911708498

springcloud-spring cloud config统一配置中心

统一配置中心 为什么需要统一配置中心? 统一配置中心顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护 统一配置中心的架构图: 服务者消费者集群,路由集群Zuul的配置文件可以全部交由config管理,Eureka Server配置是绝对不行的,因为Config配置中心也是作为一个Client服务注册到Eureka Server的,先有Server. 1.配置中心服务器的开发 1.添

微服务统一配置中心原理

为什么微服务要统一配置中心? 1:传统直接在properties里配置会导致任何人都能看到商户密钥,任何员工都可以利用这个密钥白嫖公司的,而且我们也不知道是谁在白嫖; 2:接口升级变更了,传统的写法需要在每个properties里修改,会很麻烦; 3:公司的一些通用配置也需要在每个project里配,重复且不利于修改; 解决:用zookeeper 原文地址:https://www.cnblogs.com/mlzdev/p/11317984.html

springboot配置文件抽离--git管理统一配置中心

springboot配置文件抽离,便于服务器读取对应配置文件,避免项目频繁更改配置文件,影响项目的调试与发布 1.创建统一配置中心项目conifg 1)pom配置依赖 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.6.RELEASE</ver

Spring Boot 入门(四)微服务之 Config Server 统一配置中心

一.目录结构 二.pom文件 <!-- 配置服务依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <!-- Eureka 客户端依赖--> <dependency> <groupId&

SpringCloud(3) Config统一配置中心

config会从git拉取配置文件到本地,然后读取本地文件 eureka: client: service-url: #注册服务端地址 defaultZone: http://localhost:8761/eureka spring: application: name: config cloud: config: server: git: #配置文件git地址 uri: https://gitee.com/yejiaomin/config-repo #git用户名密码 username: xx

.net 分布式架构之配置中心

.net 统一配置中心 用于所有项目统一的配置集中管理,简化运维和项目部署,具备高灵活性,高性能,高稳定性,高及时性.同时具备配置项的负载均衡和故障转移,从而实现项目的软性的负载均衡和故障转移能力. 1.高灵活性一个项目可以自由组合或者继承多个分类配置,多个分类的相同配置项可以被子分类配置项覆盖.(举例:项目A可以组合使用"基础数据库配置","基础监控配置","基础日志配置","项目A配置";项目A配置可以定义自身的特殊配置和