系列文章目录
二.SpringCloud源码剖析-Eureka Client 初始化过程
五.SpringCloud源码剖析-Eureka Client服务续约
六.SpringCloud源码剖析-Eureka Client取消注册
七.SpringCloud源码剖析-Eureka Server的自动配置
八.SpringCloud源码剖析-Eureka Server初始化流程
九.SpringCloud源码剖析-Eureka Server服务注册流程
十.SpringCloud源码剖析-Eureka Server服务续约
十一.SpringCloud源码剖析-Eureka Server服务注册表拉取
十二.SpringCloud源码剖析-Eureka Server服务剔除
十三.SpringCloud源码剖析-Eureka Server服务下线
# 前言 前面的章节我们针对于Eureak Client的初始化 ,服务注册 ,服务发现,服务续约,取消注册功能进行了分析,接下来我们围绕Eureka的核心功能对Server端进行分析,本章将会分析Eureka Server的启动过程。
1.EureakServerAutoConfiguration的注册
这里和EureakClientAutoConfiguration
差不多,都是由主启动类上的@SpringBootApplication
标签中的@EnableAutoConfiguration
启动自动配置,通过AutoConfigurationImportSelector
来扫描classpath下的starter包中的自动配置类
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration //开启自动配置
@ComponentScan(
excludeFilters = {
@Filter(
type = FilterType.CUSTOM,
classes = {
TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {
AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@EnableAutoConfiguration标签的源码
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({
AutoConfigurationImportSelector.class}) //自动配置选择器
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {
};
String[] excludeName() default {
};
}
AutoConfigurationImportSelector导入选择器源码
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
...省略部分代码...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//1.这里会扫描spring.factories文件中的配置
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
//2.加载一堆自动配置,其中就有 EureakServerAutoConfiguration
return StringUtils.toStringArray(configurations);
}
//获取候选的自动配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//通过SpringFactoriesLoader加载spring.spring.factories中的配置类
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
//从错误日志就可以看得出在扫描 META-INF/spring.factories
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
这里的 selectImports
方法会通过getCandidateConfigurations
方法使用SpringFactoriesLoader去加载classpath下的jar中的META-INF/spring.factories
文件,该文件中有很多的自动配置,其中 EureakServerAutoConfiguration
就在spring-cloud-netflix-eureka-server-2.0.1.RELEASE.jar
中如下
2.EureakServerAutoConfiguration被加载的条件
在EureakServerAutoConfiguration
自动配置类上有一个注册条件
/**
* @author Gunnar Hillert
* @author Biju Kunjummen
* @author Fahim Farook
*/
@Configuration
//导入 EurekaServerInitializerConfiguration 对Eureak进行初始化
@Import(EurekaServerInitializerConfiguration.class)
//条件:EurekaServerMarkerConfiguration.Marker是通过@EnableEureakServer激活
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
//Eureka仪表盘界面配置,以及实例注册的配置
@EnableConfigurationProperties({
EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
//加载server.properties 服务端配置文件
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
这个条件是通过@EnableEureakServer开启,
/**
激活 EurekaServerAutoConfiguration
* Annotation to activate Eureka Server related configuration {@link EurekaServerAutoConfiguration}
*
* @author Dave Syer
* @author Biju Kunjummen
*
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}
EnableEurekaServer
的注释写的很清楚,该注解用来激活EureakServerAutoConfiguration
配置,通过@Import(EurekaServerMarkerConfiguration.class)
来激活的
/**
* Responsible for adding in a marker bean to activate
* {@link EurekaServerAutoConfiguration}
*
* @author Biju Kunjummen
*/
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}
看到这里就清楚了,EnableEurekaServer通过@Import(EurekaServerMarkerConfiguration.class)
导入EurekaServerMarkerConfiguration.Marker
,而在EureakServerAutoConfiguration
配置类上正好有一个条件@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class),
满足这个条件,自动配置被加载,他们的加载关系如下:
3.EureakServerAutoConfiguration配置了什么?
EureakServerAutoConfiguration
配置了那些东西?我们来看一下源码,哦~~~,特别说明一下,服务端的pom我用的是spring-cloud-starter-netflix-eureka-server
,该依赖会把客户端的包spring-cloud-starter-netflix-eureka-client
也导入进来(Eureka集群需要互相注册),所以你会看到下面的配置类中也会有eureka client的配置
/**
* @author Gunnar Hillert
* @author Biju Kunjummen
* @author Fahim Farook
*/
@Configuration
//导入 EurekaServerInitializerConfiguration 对Eureak进行初始化
@Import(EurekaServerInitializerConfiguration.class)
//条件:EurekaServerMarkerConfiguration.Marker是通过@EnableEureakServer激活
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
//Eureka仪表盘界面配置,以及实例注册的配置
@EnableConfigurationProperties({
EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
//加载server.properties 服务端配置文件
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
/**
* List of packages containing Jersey resources required by the Eureka server
*/
private static final String[] EUREKA_PACKAGES = new String[] {
"com.netflix.discovery",
"com.netflix.eureka" };
@Autowired
private ApplicationInfoManager applicationInfoManager;
//eureka server服务端配置对象加载 eureka.server 开头的配置项
//里面记录了了EurekaServer所需要的配置
@Autowired
private EurekaServerConfig eurekaServerConfig;
//eureka client 客户端配置对象 加载eureka.client开头的配置项
@Autowired
private EurekaClientConfig eurekaClientConfig;
//Eureka客户端
@Autowired
private EurekaClient eurekaClient;
//实例注册表属性配置,加载eureka.instance.registry开头的配置
@Autowired
private InstanceRegistryProperties instanceRegistryProperties;
public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();
@Bean
public HasFeatures eurekaServerFeature() {
return HasFeatures.namedFeature("Eureka Server",
EurekaServerAutoConfiguration.class);
}
//配置EurekaServerConfig ,Eureka服务器配置,加载eureka.server开头配置
@Configuration
protected static class EurekaServerConfigBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
EurekaServerConfigBean server = new EurekaServerConfigBean();
//如果开启了eureka.client.registerWithEureka=true 注册到Eureka
if (clientConfig.shouldRegisterWithEureka()) {
// Set a sensible default if we are supposed to replicate
//当eureka服务器启动时尝试去获取集群里其他服务器上的注册信息的次数,默认为5
server.setRegistrySyncRetries(5);
}
return server;
}
}
//定义Eureka Server dashboard仪表盘监控界面的端点,注册中心的监控界面就是在这里定义的默认是“/”
@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}
static {
CodecWrappers.registerWrapper(JACKSON_JSON);
EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
}
@Bean
public ServerCodecs serverCodecs() {
return new CloudServerCodecs(this.eurekaServerConfig);
}
private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) {
CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName());
return codec == null ? CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec;
}
private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) {
CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getXmlCodecName());
return codec == null ? CodecWrappers.getCodec(CodecWrappers.XStreamXml.class)
: codec;
}
class CloudServerCodecs extends DefaultServerCodecs {
public CloudServerCodecs(EurekaServerConfig serverConfig) {
super(getFullJson(serverConfig),
CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class),
getFullXml(serverConfig),
CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class));
}
}
// 注册PeerAwareInstanceRegistry,应用对象注册表接口,提供了Eureka群内注册信息的同步功能。
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
ServerCodecs serverCodecs) {
//客户端获取注册列表,强制初始化EurekaClient
this.eurekaClient.getApplications(); // force initialization
//创建实例注册器InstanceRegistry,继承了PeerAwareInstanceRegistry,PeerAwareInstanceRegistryimpl的实现
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
serverCodecs, this.eurekaClient,
//期望最大每分钟续租次数
this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
//PeerEurekaNode的工具类,用来管理PeerEurekaNode的生命周期,PeerEurekaNode表示集群中的一个节点
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
ServerCodecs serverCodecs) {
return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}
/**
//可刷新的集群节点,通过监听EnvironmentChangeEvent环境改变事件,如果特点的配置改变如:
//eureka.client.region,eureka.client.service-url配置改变就刷新集群中的节点信息
* {@link PeerEurekaNodes} which updates peers when /refresh is invoked.
* Peers are updated only if
* <code>eureka.client.use-dns-for-fetching-service-urls</code> is
* <code>false</code> and one of following properties have changed.
* </p>
* <ul>
* <li><code>eureka.client.availability-zones</code></li>
* <li><code>eureka.client.region</code></li>
* <li><code>eureka.client.service-url.<zone></code></li>
* </ul>
*/
static class RefreshablePeerEurekaNodes extends PeerEurekaNodes
implements ApplicationListener<EnvironmentChangeEvent> {
public RefreshablePeerEurekaNodes(
final PeerAwareInstanceRegistry registry,
final EurekaServerConfig serverConfig,
final EurekaClientConfig clientConfig,
final ServerCodecs serverCodecs,
final ApplicationInfoManager applicationInfoManager) {
super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager);
}
//监听事件
@Override
public void onApplicationEvent(final EnvironmentChangeEvent event) {
//如果配置发生改变
if (shouldUpdate(event.getKeys())) {
//更新集群中的节点,通过删除旧的不可用的PeerEurekaNode,创建新的副本
updatePeerEurekaNodes(resolvePeerUrls());
}
}
/*
检查是否需要更新节点,检查特点的配置是否有改变
* Check whether specific properties have changed.
*/
protected boolean shouldUpdate(final Set<String> changedKeys) {
assert changedKeys != null;
// if eureka.client.use-dns-for-fetching-service-urls is true, then
// service-url will not be fetched from environment.
if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
return false;
}
if (changedKeys.contains("eureka.client.region")) {
return true;
}
for (final String key : changedKeys) {
// property keys are not expected to be null.
if (key.startsWith("eureka.client.service-url.") ||
key.startsWith("eureka.client.availability-zones.")) {
return true;
}
}
return false;
}
}
//EurekaServer的上下文对象
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
registry, peerEurekaNodes, this.applicationInfoManager);
}
//EurekaServerBootstrap :EurekaServer的启动引导
@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
EurekaServerContext serverContext) {
return new EurekaServerBootstrap(this.applicationInfoManager,
this.eurekaClientConfig, this.eurekaServerConfig, registry,
serverContext);
}
/**
注册 Jersey filter ,通过ServletContainer处理/eureka/*请求
* Register the Jersey filter
*/
@Bean
public FilterRegistrationBean jerseyFilterRegistration(
javax.ws.rs.core.Application eurekaJerseyApp) {
FilterRegistrationBean bean = new FilterRegistrationBean();
//使用ServletContainer处理
bean.setFilter(new ServletContainer(eurekaJerseyApp));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
//filter的拦截url /eureka/*
bean.setUrlPatterns(
Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
return bean;
}
/**
创建用Eureka服务器所需的所有资源,构建Jersey {@link javax.ws.rs.core.Application}。
* Construct a Jersey {@link javax.ws.rs.core.Application} with all the resources
* required by the Eureka server.
*/
@Bean
public javax.ws.rs.core.Application jerseyApplication(Environment environment,
ResourceLoader resourceLoader) {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
false, environment);
// Filter to include only classes that have a particular annotation.
//
provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));
// Find classes in Eureka packages (or subpackages)
//
Set<Class<?>> classes = new HashSet<>();
for (String basePackage : EUREKA_PACKAGES) {
Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
for (BeanDefinition bd : beans) {
Class<?> cls = ClassUtils.resolveClassName(bd.getBeanClassName(),
resourceLoader.getClassLoader());
classes.add(cls);
}
}
// Construct the Jersey ResourceConfig
//
Map<String, Object> propsAndFeatures = new HashMap<>();
propsAndFeatures.put(
// Skip static content used by the webapp
ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX,
EurekaConstants.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*");
DefaultResourceConfig rc = new DefaultResourceConfig(classes);
rc.setPropertiesAndFeatures(propsAndFeatures);
return rc;
}
@Bean
public FilterRegistrationBean traceFilterRegistration(
@Qualifier("httpTraceFilter") Filter filter) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(filter);
bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return bean;
}
}
我们来归纳一下EurekaServerAutoConfiguration
配置了那些东西
@Import(EurekaServerInitializerConfiguration.class)
:导入了EurekaServer初始化配置@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
:这个是配置起效的条件,通过@EnableEurekaServer
开启该条件EurekaDashboardProperties.class
:仪表盘配置InstanceRegistryProperties.class
:以 eureka.instance.registry开头的服务注册配置@PropertySource("classpath:/eureka/server.properties")
:加载server.properties配置文件注册了
EurekaServerConfig
:Eureka服务端配置对象,eureka服务器运行所需的配置信息,实现由两个一个是EurekaServerConfigBean
是netflix的实现,一个是DefaultEurekaServerConfig
是Eureka的实现- 注册了
EurekaController
:仪表盘端点controller,默认的路径是“/” - 注册了
PeerAwareInstanceRegistry
应用对象注册接口,提供了eureka集群间的服务同步功能及相关操作 - 注册了
PeerEurekaNodes
:集群节点PeerEurekaNode
的管理类, - 注册了
EurekaServerContext
:EurekaServer的上下文对象默认实现DefaultEurekaServerContext
,提供了initialize初始化和shutdown关闭方法。 - 注册了
EurekaServerBootstrap
服务端启动引导,EurekaServer启动的关键 - 注册了
jersey Filter
:使用ServletContainer
实现功能,用来处理/eureka的请求
总结
- SpringBoot自动配置加载EurekaServerAutoConfiguration自动配置
- @EnableEurekaServer导入EurekaServerMarkerConfiguration.marker激活EurekaServerAutoConfiguration自动配置
- EurekaServerAutoConfiguration注册了一系列组件,比较重要的有EurekaServerInitializerConfiguration初始化EurekaServer配置,PeerAwareInstanceRegistry服务注册器,PeerEurekaNodes服务节PeerEurekaNode点生命周期管理工具,EurekaServerContext服务上下文,EurekaServerBootstrap EurekaServer的启动引导等等
在下一章节我们将来研究一下EurekaServer的初始化的详细过程