简述Eureka服务治理体系
在分析源码之前,我们再来简单的梳理一下Eureka服务治理体系。在整个服务治理基础架构中有三个核心要素:
- 服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能,也就是在之前我们是实现的eurekaserver。
- 服务提供者: 提供服务的应用,可以是Springboot应用,也可以是其他技术平台且遵循Eureka通讯机制的应用,他将自己提供的服务注册到Eureka,以供其他应用发现。
- 服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其所需要的服务,在上一节中使用了Ribbon来实现消费,后面还会介绍使用Feign的消费方式
很多时候,客户端即是服务提供者也是服务消费者。
源码分析
下面通过源码看一下Eureka是如何运作和配置的。
首先思考一下对于服务注册中心、服务提供者、服务消费者这三个主要元素来说,后两个也就是Eureka客户端在整个运行机制中是大部分通信行为的主动发起者,而注册中心主要是处理请求的接受者,所以,我们可以从Eureka的客户端作为入口看看它是如何完成这些主动通信行为的。
我们将一个普通的SpringBoot应用注册到EurekaServer或者从EurekaServer中获取服务列表时候,主要做了两件事:
- 在应用主类中配置了@EnableDiscoveryClient
- 在application.yaml配置文件汇总,使用defaultZone来指定服务注册中心的位置。
**注意:根据官方文档github显示,在EnableEurekaServer.java
也就是@EnableEurekaServer注解中移除@EnableDiscoveryClient注解**
所以我们先看@EnableDiscoveryClient的源码,具体如下:
@EnableDiscoveryClient
/**
* Annotation to enable a DiscoveryClient implementation.
* @author Spencer Gibb
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {
/**
* If true, the ServiceRegistry will automatically register the local server.
*/
boolean autoRegister() default true;
}
我们通过注解可以清晰的知道,这个类的主要作用是开启DiscoveryClient实例的。
同时会默认autoRegister为true;
那么我们看一下是如是实现的:
实现DiscoveryClient实例
导入EnableDiscoveryClientImportSelector类
我们都知道,@Import在Spring4.2之后,不仅可以导入配置类,也可以导入普通的java类,并将其声明成一个bean。所以这里将EnableDiscoveryClientImportSelector导入进来。
EnableDiscoveryClientImportSelector类
这个类有一个重要方法,selectImports
@Override
public String[] selectImports(AnnotationMetadata metadata) {
String[] imports = super.selectImports(metadata);
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));
boolean autoRegister = attributes.getBoolean("autoRegister");
if (autoRegister) {
List<String> importsList = new ArrayList<>(Arrays.asList(imports));
importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
imports = importsList.toArray(new String[0]);
} else {
Environment env = getEnvironment();
if(ConfigurableEnvironment.class.isInstance(env)) {
ConfigurableEnvironment configEnv = (ConfigurableEnvironment)env;
LinkedHashMap<String, Object> map = new LinkedHashMap<>();
map.put("spring.cloud.service-registry.auto-registration.enabled", false);
MapPropertySource propertySource = new MapPropertySource(
"springCloudDiscoveryClient", map);
configEnv.getPropertySources().addLast(propertySource);
}
}
return imports;
}
首先可以看到这里取到了之前定义的autoRegister为true。
其次这里面调用的他的父类的selectImports方法,所以我们看下父类selectImports方法:
父类selectImports
@Override
public String[] selectImports(AnnotationMetadata metadata) {
if (!isEnabled()) {
return new String[0];
}
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
// Find all possible auto configuration classes, filtering duplicates
// 查找所有可能的自动配置类,过滤重复
List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
if (factories.isEmpty() && !hasDefaultFactory()) {
throw new IllegalStateException("Annotation @" + getSimpleName()
+ " found, but there are no implementations. Did you forget to include a starter?");
}
if (factories.size() > 1) {
// there should only ever be one DiscoveryClient, but there might be more than
// one factory
log.warn("More than one implementation " + "of @" + getSimpleName()
+ " (now relying on @Conditionals to pick one): " + factories);
}
return factories.toArray(new String[factories.size()]);
}
这个方法里面有两行注释,第一行简单翻译一下就是:查找所有可能的自动配置类,过滤重复。所以我们合理猜测,下面的代码就是去进行查找所有可能的自动配置类,所以我们点进loadFactoryNames方法。
loadFactoryNames方法:
/**
* The location to look for factories.
* <p>Can be present in multiple JAR files.
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
/**
* Load the fully qualified class names of factory implementations of the
* given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
* class loader.
* @param factoryClass the interface or abstract class representing the factory
* @param classLoader the ClassLoader to use for loading resources; can be
* {@code null} to use the default
* @see #loadFactories
* @throws IllegalArgumentException if an error occurs while loading factory names
*/
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
这里有两点要说明:
- FACTORIES_RESOURCE_LOCATIONL:定义的配置文件路径,此时该文件指的是@EnableDiscoveryClient所在包的spring.factories文件。
- 翻译一下loadFactoryNames方法的注释:使用给定的类加载器从给定的类型的工厂实现中加载完全限定的类名。
我们看下配置文件:
#
# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.CommonsClientAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.httpclient.HttpClientConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor
其实我一开始想的是,加载其中的某个类,进行实现,实例化DiscoveryClient。但是打过断点后发现:EnableDiscoveryClientImportSelector中selectImports方法返回的imports是空。接下来的代码:
将AutoServiceRegistrationConfiguration添加到imports中,也就是这里进行了自动注册配置。
我找到AutoServiceRegistrationConfiguration类,看了源码:
AutoServiceRegistrationConfiguration
package org.springframework.cloud.client.serviceregistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author Spencer Gibb
*/
@Configuration
@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public class AutoServiceRegistrationConfiguration {
}
说实话,重点是我认为@ConditionalOnProperty注解,然后再往下我就先不写了,因为自己的思路断了。
下一章看书怎么写的。