美文网首页
Config Service

Config Service

作者: 鱼蛮子9527 | 来源:发表于2022-05-23 09:23 被阅读0次

现在服务端中配置中心起着至关重要的作用,在Spring Cloud中也提供了Config Service来扮演者配置中心的角色,开源项目中还有Nacos等常用的配置中心,这次我们就讨论下Spring Cloud下配置中心的加载以及更新过程等。

Bootstrap Application

在Spring Cloud中经常使用bootstrap.properties(yaml)配置文件,首先来说下bootstrap.properties与application.properties的区别。在Spring Cloud应用中会创建一个Bootstrap Context作为主应用的父Context。Bootstrap Context读取bootstrap.properties负责从外部的配置中心中加载、解析配置信息,这两个Context共享一个Enviroment。Bootstrap里面的属性会优先加载,它们默认不能被本地配置覆盖。

通过在spring.factories文件中配置org.springframework.cloud.bootstrap.BootstrapConfiguration可以在引导过程中设置一些自定义信息。这些操作将在ApplicatioinContext初始化之前处理,可以通过@Order指定初始化顺序。注意在使用的时候要使用特殊的包,以保证不要被@ComponentScan注解扫描到而导致重复加载。引导过程添加额外配置的默认属性源是从Spring Cloud Config Server中加载的,可以通过添加类型为PropertySourceLocator的Bean到BootstrapContext中来实现从其他源加载配置。

Refresh Scope

Spring Cloud中增加了@RefreshScope注解来支持对类属性的变化刷新,配置信息的变化主要通过对RefreshEvent事件的监听来实现。下面我们主要分下Spring Cloud下@RefreshScope+@Value实现配置动态更新的过程。

整体处理调用过程如下表所示:

RefreshEvent监听
RefreshEventListener
onApplicationEvent()
ContextRefresher#refresh()
refreshEnvironment()
addConfigFilesToEnvironment()
ConfigurableApplicationContext#publishEvent()
RefreshScope#refreshAll()
destroy()
ApplicationContext#publishEvent()
@RefreshScope注解对象的特殊处理
RefreshScope
GenericScope
postProcessBeanDefinitionRegistry()
LockedScopedProxyFactoryBean
setBeanFactory()
super.setBeanFactory()
@RefreshScope属性访问
LockedScopedProxyFactoryBean#invoke()
SimpleBeanTargetSource#getTarget()
AbstractApplicationContext#getBean()
GenericScope#get()
BeanLifecycleWrapper#getBean()
ReflectionUtils#invokeMethod()

RefreshEvent监听

RefreshEventListener类,实现了SmartApplicationListener,可以监听ApplicationReadyEvent以及RefreshEvent。

在onApplicationEvent()方法中,当收到RefreshEvent时候,将会调用ContextRefresher#refresh()方法。

ContextRefresher#refresh()

    public synchronized Set<String> refresh() {
        Set<String> keys = refreshEnvironment();
        this.scope.refreshAll();
        return keys;
    }

这里做了两件事情

  1. 将现有的Environment中的属性进行Copy,再通过refreshEnvironment()更新为最新的Environment信息,然后进行对比发现变化。
  2. 执行Scope的全量刷新。

refreshEnvironment()

public synchronized Set<String> refreshEnvironment() {
    Map<String, Object> before = extract(
        this.context.getEnvironment().getPropertySources());
    addConfigFilesToEnvironment();
    Set<String> keys = changes(before,
                               extract(this.context.getEnvironment().getPropertySources())).keySet();
    this.context.publishEvent(new EnvironmentChangeEvent(this.context, keys));
    return keys;
}

主要是执行了Enviroment的刷新,是通过重新创建了一个BootstrapContxt来实现的,通过对比前后Enviroment的变化发布EnvironmentChangeEvent事件。

ConfigurableApplicationContext addConfigFilesToEnvironment() {
    ConfigurableApplicationContext capture = null;
    try {
        StandardEnvironment environment = copyEnvironment(
            this.context.getEnvironment());
        SpringApplicationBuilder builder = new SpringApplicationBuilder(Empty.class)
            .bannerMode(Mode.OFF).web(WebApplicationType.NONE)
            .environment(environment);
        builder.application()
            .setListeners(Arrays.asList(new BootstrapApplicationListener(),
                                        new ConfigFileApplicationListener()));
        capture = builder.run();

        MutablePropertySources target = this.context.getEnvironment().getPropertySources();
        String targetName = null;
        for (PropertySource<?> source : environment.getPropertySources()) {
            String name = source.getName();
            if (target.contains(name)) {
                targetName = name;
            }
            if (!this.standardSources.contains(name)) {
                if (target.contains(name)) {
                    target.replace(name, source);
                }
                else {
                    if (targetName != null) {
                        target.addAfter(targetName, source);
                        targetName = name;
                    }
                    else {
                        target.addFirst(source);
                        targetName = name;
                    }
                }
            }
        }
    } finally {
        ConfigurableApplicationContext closeable = capture;
        while (closeable != null) {
            closeable.close();
            if (closeable.getParent() instanceof ConfigurableApplicationContext) {
                closeable = (ConfigurableApplicationContext) closeable.getParent();
            }
            else {
                break;
            }
        }
    }
    return capture;
}

addConfigFilesToEnvironment() --> 通过SpringApplicationBuilder,以及copy的environment创建了一个新的最简ApplicationContext,这样就会触发重新加载引导过程,也就会从配置中心中读取到最新的配置,加载到Enviroment中(这也是为什么Spring Boot使用Bootstrap Context来引导启动的原因,这样可以保证主Context正常运行,只加载引导部分)。然后将最新加载的Enviroment的属性替换应用现有的ApplicationContext中的Enviroment的属性,来达到Enviroment更新的目的,最后将新创建的Application Context销毁掉。

ConfigurableApplicationContext#publishEvent() --> 发布Enviroment中变化的key的EnvironmentChangeEvent事件。

RefreshScope#refreshAll()

主要执行了缓存的清除,以及RefreshScopeRefreshedEvent事件的发布。

destroy() -> 执行了缓存的清除,其实就是HashMap的清空,同时执行每个缓存对象BeanLifecycleWrapper的destroy方法,清空缓存对象,this.bean = null。

ApplicationContext#publishEvent() --> 发布RefreshScopeRefreshedEvent事件。

@RefreshScope注解对象的特殊处理

RefreshScope类继承自GenericScope,而GenericScope实现了BeanDefinitionRegistryPostProcessor。所以查看postProcessBeanDefinitionRegistry()方法。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
    throws BeansException {
    for (String name : registry.getBeanDefinitionNames()) {
        BeanDefinition definition = registry.getBeanDefinition(name);
        if (definition instanceof RootBeanDefinition) {
            RootBeanDefinition root = (RootBeanDefinition) definition;
            if (root.getDecoratedDefinition() != null && root.hasBeanClass()
                && root.getBeanClass() == ScopedProxyFactoryBean.class) {
                if (getName().equals(root.getDecoratedDefinition().getBeanDefinition()
                                     .getScope())) {
                    root.setBeanClass(LockedScopedProxyFactoryBean.class);
                    root.getConstructorArgumentValues().addGenericArgumentValue(this);
                    // surprising that a scoped proxy bean definition is not already
                    // marked as synthetic?
                    root.setSynthetic(true);
                }
            }
        }
    }
}

方法中,判断BeanDefinition的Scope与当前对象的Scope是否一致(当前的为refresh),如果一致则将BeanDefinition的BeanClass更新为LockedScopedProxyFactoryBean,看类名也知道是这个操作是将原先直接获取类对象,更改为从ProxyFactoryBean中获取。

LockedScopedProxyFactoryBean

LockedScopedProxyFactoryBean类继承自ScopedProxyFactoryBean,实现了FactoryBean,MethodInterceptor接口。

public void setBeanFactory(BeanFactory beanFactory) {
    super.setBeanFactory(beanFactory);
    Object proxy = getObject();
    if (proxy instanceof Advised) {
        Advised advised = (Advised) proxy;
        advised.addAdvice(0, this);
    }
}

首先查看setBeanFactory()方法,这里首先调用了父类的setBeanFactory()方法,然后将自己作为增强加入到增强中。

 public void setBeanFactory(BeanFactory beanFactory) {
     if (!(beanFactory instanceof ConfigurableBeanFactory)) {
         throw new IllegalStateException("Not running in a ConfigurableBeanFactory: " + beanFactory);
     } else {
         ConfigurableBeanFactory cbf = (ConfigurableBeanFactory)beanFactory;
         this.scopedTargetSource.setBeanFactory(beanFactory);
         ProxyFactory pf = new ProxyFactory();
         pf.copyFrom(this);
         pf.setTargetSource(this.scopedTargetSource);
         Assert.notNull(this.targetBeanName, "Property 'targetBeanName' is required");
         Class<?> beanType = beanFactory.getType(this.targetBeanName);
         if (beanType == null) {
             throw new IllegalStateException("Cannot create scoped proxy for bean '" + this.targetBeanName + "': Target type could not be determined at the time of proxy creation.");
         } else {
             if (!this.isProxyTargetClass() || beanType.isInterface() || Modifier.isPrivate(beanType.getModifiers())) {
                 pf.setInterfaces(ClassUtils.getAllInterfacesForClass(beanType, cbf.getBeanClassLoader()));
             }

             ScopedObject scopedObject = new DefaultScopedObject(cbf, this.scopedTargetSource.getTargetBeanName());
             pf.addAdvice(new DelegatingIntroductionInterceptor(scopedObject));
             pf.addInterface(AopInfrastructureBean.class);
             this.proxy = pf.getProxy(cbf.getBeanClassLoader());
         }
     }
 }

super.setBeanFactory() --> 首先构建了ProxyFactory,设置targetSource为SimpleBeanTargetSource,这个操作很重要,因为SimpleBeanTargetSource#getTarget()方法每次执行时候会从BeanFactory中获取最新的Bean,对实现动态更新属性很重要。之后通过执行this.proxy = pf.getProxy(cbf.getBeanClassLoader());构建了proxy对象。

advised.addAdvice(0, this) --> 由于LockedScopedProxyFactoryBean同时实现了MethodInterceptor接口,所以可以作为增强加入到对象的动态代理处理中。

@RefreshScope属性访问

当发生@RefreshScope注解对象方法调用时候,由于每个对象都被包装成了LockedScopedProxyFactoryBean,需要看相关的增加类,也就是前面将自身加入到增强中的LockedScopedProxyFactoryBean的invoke()方法。

public Object invoke(MethodInvocation invocation) throws Throwable {
    Method method = invocation.getMethod();
    if (AopUtils.isEqualsMethod(method) || AopUtils.isToStringMethod(method)
            || AopUtils.isHashCodeMethod(method)
            || isScopedObjectGetTargetObject(method)) {
        return invocation.proceed();
    }
    Object proxy = getObject();
    ReadWriteLock readWriteLock = this.scope.getLock(this.targetBeanName);
    if (readWriteLock == null) {
        readWriteLock = new ReentrantReadWriteLock();
    }
    Lock lock = readWriteLock.readLock();
    lock.lock();
    try {
        if (proxy instanceof Advised) {
            Advised advised = (Advised) proxy;
            ReflectionUtils.makeAccessible(method);
            return ReflectionUtils.invokeMethod(method,
                    advised.getTargetSource().getTarget(),
                    invocation.getArguments());
        }
        return invocation.proceed();
    }
    finally {
        lock.unlock();
    }
}

这里首先会根据ReadWriteLock判断当前的操作是否处于锁定。之后主要是看这一个调用ReflectionUtils.invokeMethod(method, advised.getTargetSource().getTarget(), invocation.getArguments());

advised.getTargetSource().getTarget()

这个核心理解advised.getTargetSource().getTarget(),advised.getTargetSource()就是上面设置的SimpleBeanTargetSource对象,而SimpleBeanTargetSource#getTarget()方法,其实就是调用了BeanFactory#getBean()方法。

AbstractBeanFactory#getBean() --> AbstractBeanFactory#doGetBean(),这里会做如下判断

if (mbd.isSingleton()) {
    ......
}

else if (mbd.isPrototype()) {
    ......
}

else {
    String scopeName = mbd.getScope();
    Scope scope = this.scopes.get(scopeName);
    try {
        Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
                return createBean(beanName, mbd, args);
            }
            finally {
                afterPrototypeCreation(beanName);
            }
        });
        bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    }
}

很显然,分支会走到else中。这里获取到的Scope也就是之前提到过的RefreshScope对象,然后执行RefreshScope#get()方法。

RefreshScope#get() --> 首先从缓存中获取BeanLifecycleWrapper对象,然后调用其getBean()方法。

BeanLifecycleWrapper#getBean() --> 在这个方法中,可以看到如果bean不为空直接返回,如果持有的bean对象如果为空,则会objectFactory中获取。而objectFactory的getObject()就是上面代码中的匿名函数,也就是会调用AbstractApplicationContext#createBean(beanName, mbd, args)创建一个新的对象。

RefreshScope#refreshAll()的时候会将BeanLifecycleWrapper的对象置为null,到这里的时候就会创建一个新的对象,由于新的对象创建会使用当前最新的配置,也就实现了配置的刷新。

ReflectionUtils#invokeMethod()

在获取到了最新的可执行对象后,通过反射调用,自然也可以获取到最新的执行结果。

可以看到SpringCloud的实现机制还是比较复杂的,使用动态代理增加了理解的复杂性。为了达到属性更新能及时获取到,使用动态代理来保证每次方法调用时候,都从BeanFactory中获取最新的对象,调用过程复杂,也使用了不少锁。在性能上不会是最优的。

Cloud Native Applications

相关文章

网友评论

      本文标题:Config Service

      本文链接:https://www.haomeiwen.com/subject/dzqrprtx.html