千家信息网

Apollo客户端设计原理的源码解析是怎样的

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,这期内容当中小编将会给大家带来有关 Apollo客户端设计原理的源码解析是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1. 设计原理图 1 简要描述了 Ap
千家信息网最后更新 2026年11月04日Apollo客户端设计原理的源码解析是怎样的

这期内容当中小编将会给大家带来有关 Apollo客户端设计原理的源码解析是怎样的,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

1. 设计原理

图 1 简要描述了 Apollo 客户端的实现原理。

  • 客户端和服务端保持了一个长连接,编译配置的实时更新推送。

  • 定时拉取配置是客户端本地的一个定时任务,默认为每 5 分钟拉取一次,也可以通过在运行时指定 System Property:apollo.refreshInterval 来覆盖,单位是分钟,推送+定时拉取=双保险。

  • 客户端从 Apollo 配置中心服务端获取到应用的最新配置后,会保存在内存中。

  • 客户端会把从服务端获取到的配置在本地文件系统缓存一份,当服务或者网络不可用时,可以使用本地的配置,也就是我们的本地开发模式 env=Local。

2. 和 Spring 集成的原理

Apollo 除了支持 API 方式获取配置,也支持和 Spring/Spring Boot 集成,集成后可以直接通过 @Value 获取配置,我们来分析下集成的原理。

Spring 从 3.1 版本开始增加了 ConfigurableEnvironment 和 PropertySource:

  • ConfigurableEnvironment 实现了 Environment 接口,并且包含了多个 Property-Source。

  • PropertySource 可以理解为很多个 Key-Value 的属性配置,在运行时的结构形如图 2 所示。

需要注意的是,PropertySource 之间是有优先级顺序的,如果有一个 Key 在多个 property source 中都存在,那么位于前面的 property source 优先。

集成的原理就是在应用启动阶段,Apollo 从远端获取配置,然后组装成 PropertySource 并插入到第一个即可,如图 3 所示。

3. 启动时初始化配置到 Spring

客户端集成 Spring 的代码分析,我们也采取简化的方式进行讲解。

首先我们来分析,在项目启动的时候从 Apollo 拉取配置,是怎么集成到 Spring 中的。创建一个 PropertySourcesProcessor 类,用于初始化配置到 Spring PropertySource 中。具体代码如下所示。

@Componentpublic class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware {    String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources";private ConfigurableEnvironment environment;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 启动时初始化配置到Spring PropertySourceConfig config = new Config();        ConfigPropertySource configPropertySource = new ConfigPropertySource("ap-plication", config);        CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);        composite.addPropertySource(configPropertySource);        environment.getPropertySources().addFirst(composite);    }@Overridepublic void setEnvironment(Environment environment) {this.environment = (ConfigurableEnvironment) environment;    }}

实现 EnvironmentAware 接口是为了获取 Environment 对象。实现 BeanFactory-Post-Processor 接口,我们可以在容器实例化 bean 之前读取 bean 的信息并修改它。

Config 在 Apollo 中是一个接口,定义了很多读取配置的方法,比如 getProperty:getIntProperty 等。通过子类去实现这些方法,在这里我们就简化下,直接定义成一个类,提供两个必要的方法,具体代码如下所示。

public class Config {public String getProperty(String key, String defaultValue) {if (key.equals("bianchengName")) {return "C语言中文网";        }return null;    }public Set getPropertyNames() {        Set names = new HashSet<>();        names.add("bianchengName");return names;    }}

Config 就是配置类,配置拉取之后会存储在类中,所有配置的读取都必须经过它,我们在这里就平格定义需要读取的 key 为 bianchengName。

然后需要将 Config 封装成 PropertySource 才能插入到 Spring Environment 中。

定义一个 ConfigPropertySource 用于将 Config 封装成 PropertySource,ConfigProperty-Source 继承了 EnumerablePropertySource,EnumerablePropertySource 继承了 PropertySource。具体代码如下所示。

public class ConfigPropertySource extends EnumerablePropertySource {private static final String[] EMPTY_ARRAY = new String[0];    ConfigPropertySource(String name, Config source) {super(name, source);    }@Overridepublic String[] getPropertyNames() {        Set propertyNames = this.source.getPropertyNames();if (propertyNames.isEmpty()) {return EMPTY_ARRAY;        }return propertyNames.toArray(new String[propertyNames.size()]);    }@Overridepublic Object getProperty(String name) {return this.source.getProperty(name, null);    }}

需要做的操作还是重写 getPropertyNames 和 getProperty 这两个方法。当调用这两个方法时,返回的就是 Config 中的内容。

最后将 ConfigPropertySource 添加到 CompositePropertySource 中,并且加入到 Confi-gu-rable-Environment 即可。

定义一个接口用来测试有没有效果,具体代码如下所示。

@RestControllerpublic class ConfigController {@Value("${bianchengName:zhangsan}")private String name;@GetMapping("/get")private String bianchengUrl;@GetMapping("/get")public String get() {return name + bianchengUrl;    }}

在配置文件中增加对应的配置:

bianchengName=xxxbianchengUrl=http://minglisoft.cn/cloud

在没有增加上面讲的代码之前,访问 /get 接口返回的是 xxxhttp://c.biancheng.net。加上上面讲解的代码之后,返回的内容就变成了猿天地 http://c.biancheng.net。

这是因为我们在 Config 中对应 bianchengName 这个 key 的返回值是猿天地,也间接证明了在启动的时候可以通过这种方式来覆盖本地的值。这就是 Apollo 与 Spring 集成的原理。

4. 运行中修改配置如何刷新

在这一节中,我们来讲解下在项目运行过程中,配置发生修改之后推送给了客户端,那么这个值如何去更新 Spring 当中的值呢?

原理就是把这些配置都存储起来,当配置发生变化的时候进行修改就可以。Apollo 中定义了一个 SpringValueProcessor 类,用来处理 Spring 中值的修改。下面只贴出一部分代码,如下所示。

@Componentpublic class SpringValueProcessor implements BeanPostProcessor, BeanFactoryAware {private PlaceholderHelper placeholderHelper = new PlaceholderHelper();private BeanFactory beanFactory;public SpringValueRegistry springValueRegistry = new SpringValueRegistry();@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        Class clazz = bean.getClass();for (Field field : findAllField(clazz)) {            processField(bean, beanName, field);        }return bean;    }private void processField(Object bean, String beanName, Field field) {// register @Value on fieldValue value = field.getAnnotation(Value.class);if (value == null) {return;        }        Set keys = placeholderHelper.extractPlaceholderKeys(value.value());if (keys.isEmpty()) {return;        }for (String key : keys) {            SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);            springValueRegistry.register(beanFactory, key, springValue);        }    }}

通过实现 BeanPostProcessor 来处理每个 bean 中的值,然后将这个配置信息封装成一个 SpringValue 存储到 springValueRegistry 中。

SpringValue 代码如下所示。

public class SpringValue {private MethodParameter methodParameter;private Field field;private Object bean;private String beanName;private String key;private String placeholder;private Class targetType;private Type genericType;private boolean isJson;}

SpringValueRegistry 就是利用 Map 来存储,代码如下所示。

public class SpringValueRegistry {private final Map> registry = Maps.newConcurrentMap();private final Object LOCK = new Object();public void register(BeanFactory beanFactory, String key, SpringValue springValue) {if (!registry.containsKey(beanFactory)) {            synchronized (LOCK) {if (!registry.containsKey(beanFactory)) {                    registry.put(beanFactory, LinkedListMultimap.create());                }            }        }        registry.get(beanFactory).put(key, springValue);    }public Collection get(BeanFactory beanFactory, String key) {        Multimap beanFactorySpringValues = registry.get(beanFactory);if (beanFactorySpringValues == null) {return null;        }return beanFactorySpringValues.get(key);    }}

写个接口用于模拟配置修改,具体代码如下所示。

@RestControllerpublic class ConfigController {@Autowiredprivate SpringValueProcessor springValueProcessor;@Autowiredprivate ConfigurableBeanFactory beanFactory;@GetMapping("/update")public String update(String value) {        Collection targetValues = springValueProcessor.springValueRegistry.get(beanFactory,"bianchengName");for (SpringValueval : targetValues) {try {val.update(value);            } catch (IllegalAccessException | InvocationTargetException e) {                e.printStackTrace();            }        }return name;    }}

当我们调用 /update接口后,在前面的 /get 接口可以看到猿天地的值改成了你传入的那个值,这就是动态修改。

通过阅读 Apollo 的源码,可以学到很多的东西。

  • Apollo 用的是 Mysql。Apollo 是多个库,通过库来区分不同环境下的配置。

  • Apollo 基于 Http 长连接实现推送,还有容灾的定时拉取逻辑。

  • Apollo 也有自己的原生获取值的对象,同时还集成到了 Spring 中,可以兼容老项目的使用方式。

  • Apollo 中可以直接使用注解来进行监听,非常方便。

上述就是小编为大家分享的 Apollo客户端设计原理的源码解析是怎样的了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

配置 代码 原理 客户 接口 客户端 就是 方法 分析 内容 方式 存储 服务 运行 源码 设计 两个 多个 天地 时候 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 南京欣网互联网络科技有限公司 科技金融是不是互联网金融 互联网科技巨头监管 美国互联网保险保险科技 互联网科技深圳 工业互联网科技新基建 科技类互联网 伴你行互联网科技有限公司 互联网科技大会 银川新华互联网科技学校地址 互联网科技中介 承德科技产业互联网发展 智腾互联网科技有限公司 知域互联网科技有限公司大骗子 宜昌元轩互联网科技有限公司 中和天地互联网科技有限公司 为客找房互联网科技公司怎么样 工业互联网展湾科技股份有限公司 江苏互联网智能科技软件 张家口科技产业互联网平台 互联网科技公司发展 深圳期待互联网科技 互联网汽车科技小镇 字节跳动和互联网科技 团飞科技与伯堂互联网服务平台 湖南诚度互联网科技有限公司 互联网科技公司实习生 乌鲁木齐新华智原互联网科技学校 世界互联网领先科技成果获奖 平安好医生打造互联网科技医疗
0