1、服务端实时推送设计
新配置发布的大致流程
2. 发送 ReleaseMessage 的实现方式
ReleaseMessage 消息是通过 MySQL 实现了一个简单的消息队列。之所以没有采用消息中间件,是为了让 Apollo 在部署的时候尽量简单,尽可能减少外部依赖
发送 ReleaseMessage 的实现方式
3. Config Service 通知客户端的实现方式
通知采用基于 Http 长连接实现,主要分为下面几个步骤:
1、设计原理
Apollo 客户端的实现原理
2、与 Spring 集成后通过 @Value 获取配置原理
Spring 从 3.1 版本开始增加了 ConfigurableEnvironment 和 PropertySource:
Spring 配置结构
需要注意的是,PropertySource 之间是有优先级顺序的,如果有一个 Key 在多个 property source 中都存在,那么位于前面的 property source 优先。
集成的原理就是在应用启动阶段,Apollo 从远端获取配置,然后组装成 PropertySource 并插入到第一个即可,如下图所示。
3、动态刷新配置的实现原理
Apollo Client 中定义了 SpringValueProcessor 类,其实现了 BeanPostProcessor 用于处理值修改。
public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {
private PlaceholderHelper placeholderHelper = new PlaceholderHelper();
private BeanFactory beanFactory;
public SpringValueRegistry springValueRegistry = new SpringValueRegistry();
@Override
public 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 field
Value value = field.getAnnotation(Value.class);
if (value == null) {
return;
}
Set<String> 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);
logger.debug("Monitoring {}", 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<BeanFactory, Multimap<String, SpringValue>> 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.<String, SpringValue>create());
}
}
}
registry.get(beanFactory).put(key, springValue);
}
// 获取
public Collection<SpringValue> get(BeanFactory beanFactory, String key) {
Multimap<String, SpringValue> beanFactorySpringValues = registry.get(beanFactory);
if (beanFactorySpringValues == null) {
return null;
}
return beanFactorySpringValues.get(key);
}
}
当
AutoUpdateConfigChangeListener 监听到变化就会调用 onChange 更新值
public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
private final SpringValueRegistry springValueRegistry;
@Override
public void onChange(ConfigChangeEvent changeEvent) {
Set<String> keys = changeEvent.changedKeys();
if (CollectionUtils.isEmpty(keys)) {
return;
}
for (String key : keys) {
// 1. check whether the changed key is relevant
Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
if (targetValues == null || targetValues.isEmpty()) {
continue;
}
// 2. update the value 更新值
for (SpringValue val : targetValues) {
updateSpringValue(val);
}
}
}
}
本文参考
http://c.biancheng.NET/view/5480.html
http://c.biancheng.net/view/5482.html