前言
前面的章节我们针对于Eureak Client的初始化 ,服务注册 ,服务发现,服务续约,取消注册功能进行了分析,接下来我们围绕Eureka的核心功能对Server端进行分析,本章将会分析Eureka Server的启动过程。
1.EureakServerAutoConfiguration的注册
这里和EureakClientAutoConfiguration
差不多,都是由主启动类上的@SpringBootApplication
标签中的@EnableAutoConfiguration
启动自动配置,通过AutoConfigurationImportSelector
来扫描classpath下的starter包中的自动配置类
ElementType.TYPE}) ({RetentionPolicy.RUNTIME) (//开启自动配置 ( excludeFilters= { ( type=FilterType.CUSTOM, classes= {TypeExcludeFilter.class} ), ( type=FilterType.CUSTOM, classes= {AutoConfigurationExcludeFilter.class} )} ) public@interfaceSpringBootApplication { }
@EnableAutoConfiguration标签的源码
ElementType.TYPE}) ({RetentionPolicy.RUNTIME) (AutoConfigurationImportSelector.class}) //自动配置选择器 ({public@interfaceEnableAutoConfiguration { StringENABLED_OVERRIDE_PROPERTY="spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
AutoConfigurationImportSelector导入选择器源码
publicclassAutoConfigurationImportSelectorimplementsDeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { ...省略部分代码... publicString[] selectImports(AnnotationMetadataannotationMetadata) { if (!isEnabled(annotationMetadata)) { returnNO_IMPORTS; } AutoConfigurationMetadataautoConfigurationMetadata=AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributesattributes=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 returnStringUtils.toStringArray(configurations); } //获取候选的自动配置protectedList<String>getCandidateConfigurations(AnnotationMetadatametadata, AnnotationAttributesattributes) { //通过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."); returnconfigurations; } }
这里的 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*///导入 EurekaServerInitializerConfiguration 对Eureak进行初始化EurekaServerInitializerConfiguration.class) (//条件:EurekaServerMarkerConfiguration.Marker是通过@EnableEureakServer激活EurekaServerMarkerConfiguration.Marker.class) (//Eureka仪表盘界面配置,以及实例注册的配置EurekaDashboardProperties.class, ({ InstanceRegistryProperties.class }) //加载server.properties 服务端配置文件"classpath:/eureka/server.properties") (publicclassEurekaServerAutoConfigurationextendsWebMvcConfigurerAdapter { }
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
这个条件是通过@EnableEureakServer开启,
/**激活 EurekaServerAutoConfiguration* Annotation to activate Eureka Server related configuration {@link EurekaServerAutoConfiguration}** @author Dave Syer* @author Biju Kunjummen**/ElementType.TYPE) (RetentionPolicy.RUNTIME) (EurekaServerMarkerConfiguration.class) (public@interfaceEnableEurekaServer { }
EnableEurekaServer
的注释写的很清楚,该注解用来激活EureakServerAutoConfiguration
配置,通过@Import(EurekaServerMarkerConfiguration.class)
来激活的
/*** Responsible for adding in a marker bean to activate* {@link EurekaServerAutoConfiguration}** @author Biju Kunjummen*/publicclassEurekaServerMarkerConfiguration { publicMarkereurekaServerMarkerBean() { returnnewMarker(); } classMarker { } }
看到这里就清楚了,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*///导入 EurekaServerInitializerConfiguration 对Eureak进行初始化EurekaServerInitializerConfiguration.class) (//条件:EurekaServerMarkerConfiguration.Marker是通过@EnableEureakServer激活EurekaServerMarkerConfiguration.Marker.class) (//Eureka仪表盘界面配置,以及实例注册的配置EurekaDashboardProperties.class, ({ InstanceRegistryProperties.class }) //加载server.properties 服务端配置文件"classpath:/eureka/server.properties") (publicclassEurekaServerAutoConfigurationextendsWebMvcConfigurerAdapter { /*** List of packages containing Jersey resources required by the Eureka server*/privatestaticfinalString[] EUREKA_PACKAGES=newString[] { "com.netflix.discovery", "com.netflix.eureka" }; privateApplicationInfoManagerapplicationInfoManager; //eureka server服务端配置对象加载 eureka.server 开头的配置项//里面记录了了EurekaServer所需要的配置privateEurekaServerConfigeurekaServerConfig; //eureka client 客户端配置对象 加载eureka.client开头的配置项privateEurekaClientConfigeurekaClientConfig; //Eureka客户端privateEurekaClienteurekaClient; //实例注册表属性配置,加载eureka.instance.registry开头的配置privateInstanceRegistryPropertiesinstanceRegistryProperties; publicstaticfinalCloudJacksonJsonJACKSON_JSON=newCloudJacksonJson(); publicHasFeatureseurekaServerFeature() { returnHasFeatures.namedFeature("Eureka Server", EurekaServerAutoConfiguration.class); } //配置EurekaServerConfig ,Eureka服务器配置,加载eureka.server开头配置protectedstaticclassEurekaServerConfigBeanConfiguration { publicEurekaServerConfigeurekaServerConfig(EurekaClientConfigclientConfig) { EurekaServerConfigBeanserver=newEurekaServerConfigBean(); //如果开启了eureka.client.registerWithEureka=true 注册到Eurekaif (clientConfig.shouldRegisterWithEureka()) { // Set a sensible default if we are supposed to replicate//当eureka服务器启动时尝试去获取集群里其他服务器上的注册信息的次数,默认为5server.setRegistrySyncRetries(5); } returnserver; } } //定义Eureka Server dashboard仪表盘监控界面的端点,注册中心的监控界面就是在这里定义的默认是“/”prefix="eureka.dashboard", name="enabled", matchIfMissing=true) (publicEurekaControllereurekaController() { returnnewEurekaController(this.applicationInfoManager); } static { CodecWrappers.registerWrapper(JACKSON_JSON); EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec()); } publicServerCodecsserverCodecs() { returnnewCloudServerCodecs(this.eurekaServerConfig); } privatestaticCodecWrappergetFullJson(EurekaServerConfigserverConfig) { CodecWrappercodec=CodecWrappers.getCodec(serverConfig.getJsonCodecName()); returncodec==null?CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec; } privatestaticCodecWrappergetFullXml(EurekaServerConfigserverConfig) { CodecWrappercodec=CodecWrappers.getCodec(serverConfig.getXmlCodecName()); returncodec==null?CodecWrappers.getCodec(CodecWrappers.XStreamXml.class) : codec; } classCloudServerCodecsextendsDefaultServerCodecs { publicCloudServerCodecs(EurekaServerConfigserverConfig) { super(getFullJson(serverConfig), CodecWrappers.getCodec(CodecWrappers.JacksonJsonMini.class), getFullXml(serverConfig), CodecWrappers.getCodec(CodecWrappers.JacksonXmlMini.class)); } } // 注册PeerAwareInstanceRegistry,应用对象注册表接口,提供了Eureka群内注册信息的同步功能。publicPeerAwareInstanceRegistrypeerAwareInstanceRegistry( ServerCodecsserverCodecs) { //客户端获取注册列表,强制初始化EurekaClientthis.eurekaClient.getApplications(); // force initialization//创建实例注册器InstanceRegistry,继承了PeerAwareInstanceRegistry,PeerAwareInstanceRegistryimpl的实现returnnewInstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, //期望最大每分钟续租次数this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount()); } //PeerEurekaNode的工具类,用来管理PeerEurekaNode的生命周期,PeerEurekaNode表示集群中的一个节点publicPeerEurekaNodespeerEurekaNodes(PeerAwareInstanceRegistryregistry, ServerCodecsserverCodecs) { returnnewRefreshablePeerEurekaNodes(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>*/staticclassRefreshablePeerEurekaNodesextendsPeerEurekaNodesimplementsApplicationListener<EnvironmentChangeEvent> { publicRefreshablePeerEurekaNodes( finalPeerAwareInstanceRegistryregistry, finalEurekaServerConfigserverConfig, finalEurekaClientConfigclientConfig, finalServerCodecsserverCodecs, finalApplicationInfoManagerapplicationInfoManager) { super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager); } //监听事件publicvoidonApplicationEvent(finalEnvironmentChangeEventevent) { //如果配置发生改变if (shouldUpdate(event.getKeys())) { //更新集群中的节点,通过删除旧的不可用的PeerEurekaNode,创建新的副本updatePeerEurekaNodes(resolvePeerUrls()); } } /*检查是否需要更新节点,检查特点的配置是否有改变* Check whether specific properties have changed.*/protectedbooleanshouldUpdate(finalSet<String>changedKeys) { assertchangedKeys!=null; // if eureka.client.use-dns-for-fetching-service-urls is true, then// service-url will not be fetched from environment.if (clientConfig.shouldUseDnsForFetchingServiceUrls()) { returnfalse; } if (changedKeys.contains("eureka.client.region")) { returntrue; } for (finalStringkey : changedKeys) { // property keys are not expected to be null.if (key.startsWith("eureka.client.service-url.") ||key.startsWith("eureka.client.availability-zones.")) { returntrue; } } returnfalse; } } //EurekaServer的上下文对象publicEurekaServerContexteurekaServerContext(ServerCodecsserverCodecs, PeerAwareInstanceRegistryregistry, PeerEurekaNodespeerEurekaNodes) { returnnewDefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager); } //EurekaServerBootstrap :EurekaServer的启动引导publicEurekaServerBootstrapeurekaServerBootstrap(PeerAwareInstanceRegistryregistry, EurekaServerContextserverContext) { returnnewEurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext); } /**注册 Jersey filter ,通过ServletContainer处理/eureka/*请求* Register the Jersey filter*/publicFilterRegistrationBeanjerseyFilterRegistration( javax.ws.rs.core.ApplicationeurekaJerseyApp) { FilterRegistrationBeanbean=newFilterRegistrationBean(); //使用ServletContainer处理bean.setFilter(newServletContainer(eurekaJerseyApp)); bean.setOrder(Ordered.LOWEST_PRECEDENCE); //filter的拦截url /eureka/*bean.setUrlPatterns( Collections.singletonList(EurekaConstants.DEFAULT_PREFIX+"/*")); returnbean; } /**创建用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.*/publicjavax.ws.rs.core.ApplicationjerseyApplication(Environmentenvironment, ResourceLoaderresourceLoader) { ClassPathScanningCandidateComponentProviderprovider=newClassPathScanningCandidateComponentProvider( false, environment); // Filter to include only classes that have a particular annotation.//provider.addIncludeFilter(newAnnotationTypeFilter(Path.class)); provider.addIncludeFilter(newAnnotationTypeFilter(Provider.class)); // Find classes in Eureka packages (or subpackages)//Set<Class<?>>classes=newHashSet<>(); for (StringbasePackage : EUREKA_PACKAGES) { Set<BeanDefinition>beans=provider.findCandidateComponents(basePackage); for (BeanDefinitionbd : beans) { Class<?>cls=ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader()); classes.add(cls); } } // Construct the Jersey ResourceConfig//Map<String, Object>propsAndFeatures=newHashMap<>(); propsAndFeatures.put( // Skip static content used by the webappServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX, EurekaConstants.DEFAULT_PREFIX+"/(fonts|images|css|js)/.*"); DefaultResourceConfigrc=newDefaultResourceConfig(classes); rc.setPropertiesAndFeatures(propsAndFeatures); returnrc; } publicFilterRegistrationBeantraceFilterRegistration( "httpTraceFilter") Filterfilter) { (FilterRegistrationBeanbean=newFilterRegistrationBean(); bean.setFilter(filter); bean.setOrder(Ordered.LOWEST_PRECEDENCE-10); returnbean; } }
我们来归纳一下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的初始化的详细过程