Nacos笔记——配置中心与服务注册发现

Nacos

Nacos是阿里云提供的一个配置中心与服务注册发现中心组件。官网:https://nacos.io/zh-cn/docs/what-is-nacos.html

相关注解

@RefreshScope
@EnableDiscoveryClient
@LoadBalance

SpringCloud结合Nacos的示例在官网中已经很清楚了,在这里我就只记录一下我自己在SpringCloud中使用Nacos遇到的问题和解答。

1. @RefreshScope是如何工作?

总结:RefreshScope其实是名为”refresh"的一个scope,它与为人熟知的Singleton、Prototype、Session这几个scope根本上都是一样的,定义Bean的作用域。而有RefreshScope管理的Bean当接收到事件通知时,会刷新scope中的bean。

相关源码:

  1. RefreshScope#postProcessBeanFactory
    RefreshScope继承GenericScope实现BeanFactoryPostProcessor,在ApplicationContext刷新过程中,通过调用postProcessBeanFactory方法,把名为”refresh"的scope注册到BeanFactory中。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
			throws BeansException {
		this.beanFactory = beanFactory;
		beanFactory.registerScope(this.name, this);
		setSerializationId(beanFactory);
	}
  1. RefreshAutoConfiguration.RefreshScopeBeanDefinitionEnhancer#postProcessBeanDefinitionRegistry
    RefreshAutoConfiguration.RefreshScopeBeanDefinitionEnhancer实现BeanDefinitionRegistryPostProcessor接口,在ApplicationContext的refresh过程中会调用postProcessBeanDefinitionRegistry方法。
    目的:调用ScopedProxyUtils.createScopedProxy方法创建某些bean的Scope代理实例,如:com.zaxxer.hikari.HikariDataSource
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
				throws BeansException {
			bindEnvironmentIfNeeded(registry);
			for (String name : registry.getBeanDefinitionNames()) {
				BeanDefinition definition = registry.getBeanDefinition(name);
				if (isApplicable(registry, name, definition)) {
					BeanDefinitionHolder holder = new BeanDefinitionHolder(definition,
							name);
					BeanDefinitionHolder proxy = ScopedProxyUtils
							.createScopedProxy(holder, registry, true);
					definition.setScope("refresh");
					if (registry.containsBeanDefinition(proxy.getBeanName())) {
						registry.removeBeanDefinition(proxy.getBeanName());
					}
					registry.registerBeanDefinition(proxy.getBeanName(),
							proxy.getBeanDefinition());
				}
			}
		}
  1. AbstractBeanFactory#doGetBean
    doGetBean方法中,会判断Bean的scope,把bean交由对应的scope对象管理。
......
		else {
					String scopeName = mbd.getScope();
					if (!StringUtils.hasLength(scopeName)) {
						throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
					}
					Scope scope = this.scopes.get(scopeName);
					if (scope == null) {
						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
					}
					try {
						Object scopedInstance = scope.get(beanName, () -> {
							beforePrototypeCreation(beanName);
							try {
								return createBean(beanName, mbd, args);
							}
							finally {
								afterPrototypeCreation(beanName);
							}
						});
						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
					}
					catch (IllegalStateException ex) {
						throw new BeanCreationException(beanName,
								"Scope '" + scopeName + "' is not active for the current thread; consider " +
								"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
								ex);
					}
				}
.......
  1. RefreshEventListener#handle
    RefreshEventListener实现SmartApplicationListener接口,会在AbstractApplicationContext#refresh刷新时作为Listener加载。
    public void refresh() throws BeansException, IllegalStateException {
    ......
    // Check for listener beans and register them.
    				registerListeners();
    ......
    }
    
    RefreshEventListener在接收到RefreshEvent之后,会调用ContextRefresher#refresh方法,最终会调用到RefreshScope#refreshAll方法
  2. RefreshScope#refreshAll方法
    refreshAll方法中,调用了destroy方法,把scope的bean实例全部销毁,当下次再访问到相关的bean时会触发重新加载,达到refresh的目的。
public void refreshAll() {
		super.destroy();
		this.context.publishEvent(new RefreshScopeRefreshedEvent());
	}

2. @LoadBalance注解是如何与Ribbon结合作负载均衡的?

总结:@LoadBalance作用与RestTemplate上,触发调用Ribbon的postProcessor会根据@LoadBalance注解找到对应的RestTemplate上,给对应的RestTemplate添加上Interceptor,在调用rest接口前作拦截。

  1. LoadBalancerAutoConfiguration#loadBalancedRestTemplateInitializerDeprecated方法
    LoadBalancerAutoConfiguration类自动注入相关的RestTemplate
    public class LoadBalancerAutoConfiguration {
    
    	@LoadBalanced
    	@Autowired(required = false)
    	private List<RestTemplate> restTemplates = Collections.emptyList();
    	......
    
    loadBalancedRestTemplateInitializerDeprecated方法中调用RestTemplateCustomizer的custom方法,把loadBalance相关的Interceptor注入到相关的RestTemplate中:
    @Bean
    	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
    			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
    		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
    			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
    				for (RestTemplateCustomizer customizer : customizers) {
    					customizer.customize(restTemplate);
    				}
    			}
    		});
    	}
    
  2. RestTemplateCustomizer
    @Bean
    		@ConditionalOnMissingBean
    		public RestTemplateCustomizer restTemplateCustomizer(
    				final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
    			return restTemplate -> {
    				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
    						restTemplate.getInterceptors());
    				list.add(loadBalancerInterceptor);
    				restTemplate.setInterceptors(list);
    			};
    		}
    

3. @Qualifier注解作用在别的注解上?

  1. 先看一下@Qualifier注解的源码:
    /**
     * This annotation may be used on a field or parameter as a qualifier for
     * candidate beans when autowiring. It may also be used to annotate other
     * custom annotations that can then in turn be used as qualifiers.
     *
     * @author Mark Fisher
     * @author Juergen Hoeller
     * @since 2.5
     * @see Autowired
     */
    @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    public @interface Qualifier {
    
    	String value() default "";
    
    }
    
    注释的意思是:
    @Qualifier注解是用于Autowired的时候作为一个条件选择器选择正确的bean实例进行注入操作。它可以用于注解另外一个注解,此时被注解的注解会变成一个选择器。
    这段话翻译得很绕啊,为什么要说到这个呢,是因为在@LoadBalance的AutoConfiguration中,对应的RestTemplate注入就用到了@Qualifier的这个特性:
    public class LoadBalancerAutoConfiguration {
    
    	@LoadBalanced
    	@Autowired(required = false)
    	private List<RestTemplate> restTemplates = Collections.emptyList();
    	......
    
    @LoadBalance:
    /**
     * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
     * @author Spencer Gibb
     */
    @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Qualifier
    public @interface LoadBalanced {
    
    }
    
    @LoadBalance被@Qualifier注解,所以@LoadBalance作为一个选择器,当Autowired的时候,会筛选有@LoadBalance的实例才会被注入。
  2. @Qualifier注解工作源码
    入口:
    AbstractAutowireCapableBeanFactory#autowireByType ->AbstractAutowireCapableBeanFactory#resolveDependency -> DefaultListableBeanFactory#resolveMultipleBeans -> DefaultListableBeanFactory#findAutowireCandidates -> 最终通过抽象方法调用到QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate方法中:
    public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
    		if (match) {
    			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
    			if (match) {
    				MethodParameter methodParam = descriptor.getMethodParameter();
    				if (methodParam != null) {
    					Method method = methodParam.getMethod();
    					if (method == null || void.class == method.getReturnType()) {
    						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
    					}
    				}
    			}
    		}
    		return match;
    	}
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值