apollo客户端配置项更新原理

405 阅读2分钟

巧用扩展点:

SpringValueProcessor实现了BeanFactoryPostProcessor接口的postProcessBeanfactory方法。其作用就是创建存放着bean名称作为key,SpringValueDefinition作为value的Multimap。

SpringValueProcessor还实现了BeanPostProcessor接口的
postProcessBeforeInitialization方法,该方法主要是将所有带有@value注解的属性或者方法,放到配置项名称作为key,SpringValue作为Value的Multimap中。SpringValue存放了属性对应对应的Field,后面通过调用field.set(bean, newVal),进行值更新。

SpringValue类结构:

public class SpringValue {

  private MethodParameter methodParameter;
  //字段
  private Field field;
  //所属的bean对象,弱引用,可以在任何时刻被回收
  private WeakReference<Object> beanRef;
  private String beanName;
  private String key;
  private String placeholder;
  private Class<?> targetType;
  private Type genericType;
  private boolean isJson;
}

更新处理:

在 Apollo 控制台进行配置修改并发布后,对应的 client 端拉取到更新后,会调用到
AutoUpdateConfigChangeListener类的onChange方法。

在调用onChange 收到对应的修改的配置信息 ConfigChangeEvent,其中包含改动的 key 和value, 则改动流程如下:

  1. 根据改动的配置的key从springValueRegistry找到对应的关联到这个key的Spring Bean 信息,如果找不到则不处理。
  2. 根据找到的 Spring Bean 信息,进行对应关联配置的更新。

代码如下:

@Override
public void onChange(ConfigChangeEvent changeEvent) {
  //获取到值发生变更的配置项
  Set<String> keys = changeEvent.changedKeys();
  if (CollectionUtils.isEmpty(keys)) {
    return;
  }
  for (String key : keys) {
    // 1. 检查变更的配置项是否被注入到了Bean里面
    Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
    if (targetValues == null || targetValues.isEmpty()) {
      continue;
    }

    // 2. 一个配置项,可能会存在于多个Bean中,依次将所有的Bean都更新掉
    for (SpringValue val : targetValues) {
      updateSpringValue(val);
    }
  }
}

private void updateSpringValue(SpringValue springValue) {
  try {
    //获取最新的值
    Object value = resolvePropertyValue(springValue);
    //将最新的值更新到Bean中
    springValue.update(value);

    logger.info("Auto update apollo changed value successfully, new value: {}, {}", value,
        springValue);
  } catch (Throwable ex) {
    logger.error("Auto update apollo changed value failed, {}", springValue.toString(), ex);
  }
}

/**
 * Logic transplanted from DefaultListableBeanFactory
 * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set, org.springframework.beans.TypeConverter)
 */
private Object resolvePropertyValue(SpringValue springValue) {
  // value will never be null, as @Value and @ApolloJsonValue will not allow that
  Object value = placeholderHelper
      .resolvePropertyValue(beanFactory, springValue.getBeanName(), springValue.getPlaceholder());

  if (springValue.isJson()) {
    value = parseJsonValue((String)value, springValue.getGenericType());
  } else {
    if (springValue.isField()) {
      // org.springframework.beans.TypeConverter#convertIfNecessary(java.lang.Object, java.lang.Class, java.lang.reflect.Field) is available from Spring 3.2.0+
      if (typeConverterHasConvertIfNecessaryWithFieldParameter) {
        value = this.typeConverter
            .convertIfNecessary(value, springValue.getTargetType(), springValue.getField());
      } else {
        value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType());
      }
    } else {
      value = this.typeConverter.convertIfNecessary(value, springValue.getTargetType(),
          springValue.getMethodParameter());
    }
  }

  return value;
}

private Object parseJsonValue(String json, Type targetType) {
  try {
    return gson.fromJson(json, targetType);
  } catch (Throwable ex) {
    logger.error("Parsing json '{}' to type {} failed!", json, targetType, ex);
    throw ex;
  }
}