手写分布式配置中心(1)

简介: 1 什么是分布式配置中心其实就是把一些配置的信息分离于自身的系统,而这些信息又能被应用实时获取得到。这里用springboot 举例子,我们都知道springboot 启动的时候,会加载resource 目录下面的application.properties 或者 application.yml。 这个时候我们把springboot 启动的时候所需要加载的配置文件 不和工程放在一起,统一管理,这个就是分布式配置中心的核心思想。

1 什是分布式配置中心

其实就是把一些配置的信息分离于自身的系统,而这些信息又能被应用实时获取得到。这里用springboot 举例子,我们都知道springboot 启动的时候,会加载resource 目录下面的application.properties 或者 application.yml。 这个时候我们把springboot 启动的时候所需要加载的配置文件 不和工程放在一起,统一管理,这个就是分布式配置中心的核心思想。

1.1 分布式配置中心有哪些组成

1.1.1, 有一个界面能操作配置

1.1.2, 数据能够持久化(防止丢失,服务下线在启动配置还是存在的)

1.1.3, 存在客户端和服务端, 客户端主动去拉去数据或者服务端主动推送数据。 并且刷新本机的配置。(核心)

1.1.4, 一些管理界面的操作日志, 权限系统等。

2,市面上主流的配置中心

2.1 阿里的 nacos

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施. 具体使用请看官网

2.2 nacos 的原理,就是service 监控配置是否发生改变,通过长链接在发送给客户端

架构原理图如下 ,这个是推送模式, 但是是基于长链接
1.png

2.3 携程的Apollo

Apollo是携程框架部研发并开源的一款生产级的配置中心产品,它能够集中管理应用在不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景。具体使用请看官网

携程的架构原理图也是和 Nacos 的是一样的, 也是长链接轮询, 服务端推送的模式。

2.3 spirgcloud config

springCloudConfig,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git/svn仓库中。

2.png

服务启动的时候config Service 会从远程git拉取配置文件,并存入到本地git文件库,当远程git不可用时,会从本地git文件库拉取配置信息. 具体的使用请以官网为准。

2.4 百度disconf

Disconf是百度开源出来的一款基于Zookeeper的分布式配置管理软件。目前很多公司都在使用,包括滴滴、百度、网易、顺丰等公司。通过简单的界面操作就可以动态修改配置属性,还是很方便的。使用Disconf后发现的一大好处是省却应用很多配置,而且配置可以自动load,实时生效。

基本原理图:当然具体的肯定要比这个更加的复杂, 这个只是主要的流程。

3.png

3如何实现自己的分布式配置中心

3.1 动态修改本地@Value注解的配置

3.2 在不同的bean 中, 相同的value 怎么同时修改。

4 具体思路

怎么动态修改@Value注解 的配置, 我们就要知道springboot 怎么加载application.ym 或者application.properties 文件的。

我这里用springboot2.1.1 的代码做示范。

首先打开

4.png

找到spring.factors 的配置文件

5.png

我们看到上面三个就是配置文件的加载器, 这里也显示了为啥properties 的优先级比yaml 的优先级高。 他是从上往下的顺序排列的啊。 但是真正的配置文件执行的还是下面的

我们点击去看一下实现类

ConfigFileApplicationListene

@Override

public void postProcessEnvironment(ConfigurableEnvironment environment,

      SpringApplication application) {

      //怎么加载资源

   addPropertySources(environment, application.getResourceLoader());

}

protected void addPropertySources(ConfigurableEnvironment environment,

      ResourceLoader resourceLoader) {

   RandomValuePropertySource.addToEnvironment(environment);

   // 把资源给load 到环境变量里面

   new Loader(environment, resourceLoader).load();

}

// 再用 propertySource 解析器给解析

Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {

   this.environment = environment;

   this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(

         this.environment);

   this.resourceLoader = (resourceLoader != null) ? resourceLoade

         : new DefaultResourceLoader();

   this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(

         PropertySourceLoader.class, getClass().getClassLoader());

}

那我们也就知道了,也就是我们要是能 EnvironmentPostProcessor ,在重写里面的方法也就可以动态的加载配置文件了。 下面我们就开始代码实现.

4.1 代码实现

那既然是这样我们就可以实现EnvironmentPostProcessor 这个类通过接口动态的加载配置文件了。

那下面就是具体的代码实现

@Autowired
ConfigurableEnvironment configurableEnvironment;


@Autowired
Environment environment;



@Test
public void test() {

    String name = environment.getProperty("name");

    System.out.printf("动态加载之前" +name);

    Map<String,String> map = new HashMap<>();

    map.put("name","嘟嘟");

    configurableEnvironment.getPropertySources().addLast(

            new OriginTrackedMapPropertySource("xxxx.xml", map)

    );

    String property = environment.getProperty("name");

    System.out.printf("动态加载之后" +property);

}

4.1.2 单元测试

最终的结果是

6.png

我们现在解决了第一个问题, 怎么动态增加环境变量

第二个问题在@value 注解上使用的怎么动态刷新啊。

那这个使用我们就需要ConfigurablePropertyResolver 这个类,来解析这个key , 在 找到@value 对应的bean 通过反射来刷新

具体代码

4.2 代码实现

public static void refreshBean(Object bean, ConfigurablePropertyResolver propertyResolver) {

    // 定义EL表达式解释器

    SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

    TemplateParserContext templateParserContext= new TemplateParserContext();



    String keyResolver, valueResolver = null;

    Object parserValue;

    // 获取真实对象属性



    Field[] declaredFields = bean.getClass().getDeclaredFields();

    boolean cglib = Arrays.stream(declaredFields).anyMatch(x -> x.getName().contains("CGLIB"));

    // 如果是cglib 代理找其父类

    if(cglib){

        declaredFields = bean.getClass().getSuperclass().getDeclaredFields();

    }



    // 遍历Bean实例所有属性

    for (Field field : declaredFields) {

        // 判断field是否含有@Value注解

        if (field.isAnnotationPresent(Value.class)) {

            // 读取Value注解占位符

            keyResolver = field.getAnnotation(Value.class).value();

            try {

                // 读取属性值

                valueResolver = propertyResolver.resolveRequiredPlaceholders(keyResolver);

                // EL表达式解析

                // 兼容形如:@Value("#{'${codest.five.url}'.split(',')}")含有EL表达式的情况

                Expression expression = spelExpressionParser.parseExpression(valueResolver, templateParserContext);

                if(field.getType() == Boolean.class){

                    parserValue =Boolean.valueOf(expression.getValue().toString());

                }

                else if(field.getType() == Integer.class){

                    parserValue =Integer.valueOf(expression.getValue().toString());

                }

                else if(field.getType() == Long.class){

                    parserValue =Long.valueOf(expression.getValue().toString());

                }else {

                    parserValue = expression.getValue(field.getType());



                }



            } catch (IllegalArgumentException e) {

                continue;

            }

            // 判断配置项是否存在

            if (Objects.nonNull(valueResolver)) {

                field.setAccessible(true);

                try {

                    field.set(bean, parserValue);

                    continue;

                } catch (IllegalAccessException e) {

                    e.printStackTrace();

                }

            }

        }

    }

我们在写一个单元测试测试

4.2.1 单元测试

7.png

8.png

@Autowired

ConfigurableEnvironment configurableEnvironment;

@Autowired

ConfigurablePropertyResolver configurablePropertyResolver;


@Autowired
Person person;


@Test
public void test() {

    System.out.printf("动态加载之前" +person.getName());

    Map<String,Object> map = new HashMap<>();

    map.put("name","嘟嘟");

    configurableEnvironment.getPropertySources().forEach( x->{

        if (x instanceof OriginTrackedMapPropertySource ) {

            Map<String,Object>  map1 = (Map<String, Object>) x.getSource();

            map1.putAll(map);

        }

    }

    );

    refreshBean(person,configurablePropertyResolver);

    System.out.printf("动态加载之后" +person.getName());

}

最后结果:

9.png

完美, 下一期我们在解决 多个bean, @value 的值一样怎么同时刷新。

相关文章
|
1月前
|
机器学习/深度学习 监控 算法
分布式光伏储能系统的优化配置方法(Matlab代码实现)
分布式光伏储能系统的优化配置方法(Matlab代码实现)
|
6月前
|
安全 网络安全 数据库
YashanDB分布式节点间SSL连接配置
本文介绍YashanDB分布式节点间SSL连接配置方法,确保通信安全。需统一为整个集群配置SSL,使用相同根证书签名的服务器证书,否则可能导致连接失败或数据库无法启动。文章详细说明了使用OpenSSL生成根证书、服务器私钥、证书及DH文件的步骤,并指导如何将证书分发至各节点。最后,通过配置数据库参数(如`din_ssl_enable`)并重启集群完成设置。注意,证书过期需重新生成以保障安全性。
|
8月前
|
SQL 数据建模 BI
【YashanDB 知识库】用 yasldr 配置 Bulkload 模式作单线程迁移 300G 的业务数据到分布式数据库,迁移任务频繁出错
问题描述 详细版本:YashanDB Server Enterprise Edition Release 23.2.4.100 x86_64 6db1237 影响范围: 离线数据迁移场景,影响业务数据入库。 外场将部分 NewCIS 的报表业务放到分布式数据库,验证 SQL 性能水平。 操作系统环境配置: 125G 内存 32C CPU 2T 的 HDD 磁盘 问题出现的步骤/操作: 1、部署崖山分布式数据库 1mm 1cn 3dn 单线启动 yasldr 数据迁移任务,设置 32 线程的 bulk load 模式 2、观察 yasldr.log 是否出现如下错
|
11月前
|
数据库
如何在Seata框架中配置分布式事务的隔离级别?
总的来说,配置分布式事务的隔离级别是实现分布式事务管理的重要环节之一,需要认真对待和仔细调整,以满足业务的需求和性能要求。你还可以进一步深入研究和实践 Seata 框架的配置和使用,以更好地应对各种分布式事务场景的挑战。
344 63
|
9月前
|
消息中间件 负载均衡 Java
如何设计一个分布式配置中心?
这篇文章介绍了分布式配置中心的概念、实现原理及其在实际应用中的重要性。首先通过一个面试场景引出配置中心的设计问题,接着详细解释了为什么需要分布式配置中心,尤其是在分布式系统中统一管理配置文件的必要性。文章重点分析了Apollo这一开源配置管理中心的工作原理,包括其基础模型、架构模块以及配置发布后实时生效的设计。此外,还介绍了客户端与服务端之间的交互机制,如长轮询(Http Long Polling)和定时拉取配置的fallback机制。最后,结合实际工作经验,分享了配置中心在解决多台服务器配置同步问题上的优势,帮助读者更好地理解其应用场景和价值。
484 18
|
分布式计算 Hadoop
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
Hadoop-27 ZooKeeper集群 集群配置启动 3台云服务器 myid集群 zoo.cfg多节点配置 分布式协调框架 Leader Follower Observer
215 1
|
存储 SQL 消息中间件
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
Hadoop-26 ZooKeeper集群 3台云服务器 基础概念简介与环境的配置使用 架构组成 分布式协调框架 Leader Follower Observer
164 0
|
Java 测试技术 Spring
分布式之配置中心
分布式之配置中心
148 1
|
UED 存储 数据管理
深度解析 Uno Platform 离线状态处理技巧:从网络检测到本地存储同步,全方位提升跨平台应用在无网环境下的用户体验与数据管理策略
【8月更文挑战第31天】处理离线状态下的用户体验是现代应用开发的关键。本文通过在线笔记应用案例,介绍如何使用 Uno Platform 优雅地应对离线状态。首先,利用 `NetworkInformation` 类检测网络状态;其次,使用 SQLite 实现离线存储;然后,在网络恢复时同步数据;最后,通过 UI 反馈提升用户体验。
335 0

热门文章

最新文章