Eureka Server版本:spring-cloud-netflix-eureka-server.2.1.0.RELEASE
1、Eureka Server激活
在SpringBoot项目启动类上添加@EnableEurekaServer 注解,即可激活eureka服务器。
EnableEurekaServer类源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {
}2、Eureka 事件
eureka有一个根抽象事件类org.springframework.context.ApplicationEvent,其继承自java的根事件类java.util.EventObject,EventObject维护了一个事件源对象,并在构造器中初始化事件源对象,源码如下:
public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
//事件源
protected transient Object source;
//构造器中初始化事件源
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
//对外暴露事件源
public Object getSource() {
return source;
}
public String toString() {
return getClass().getName() + "[source=" + source + "]";
}
}查看eureka根抽象事件类ApplicationEvent源码,我们会发现其构造器中调用了其父类EventObject的构造器,将事件源交给EventObject来维护。ApplicationEvent构造源码如下:
public ApplicationEvent(Object source) {
super(source); //调用父类构造器,用以初始化事件源对象
this.timestamp = System.currentTimeMillis();
}从源码中可知,eureka server有五种事件,这些事件都继承自ApplicationEvent根抽象事件,分别是:
EurekaServerStartedEvent - Eureka服务端启动事件
EurekaRegistryAvailableEvent - Eureka服务端可用事件
EurekaInstanceRegisteredEvent - Eureka客户端服务注册事件
EurekaInstanceRenewedEvent - Eureka客户端续约事件
EurekaInstanceCanceledEvent - Eureka客户端下线事件我们来看看这五种事件源码:
2.1、服务端启动事件 - EurekaServerStartedEvent
源码:
public class EurekaServerStartedEvent extends ApplicationEvent {
public EurekaServerStartedEvent(EurekaServerConfig eurekaServerConfig) {
super(eurekaServerConfig);
}
}从源码中,这个类很简单,只有一个构造器,其中调用父类构造器,传入了一个eureka服务器的配置对象。从上面的分析中,我们知道,此配置对象eurekaServerConfig将作为事件源,交给EventObject维护。
2.2、客户端服务注册事件 - EurekaInstanceRegisteredEvent
重要部分源码:
//客户端实例注册信息对象
private InstanceInfo instanceInfo;
//客户端实例租约间隔,默认为90秒
private int leaseDuration;
//是否同步,在eureka配置为高可用时,其中的一个服务器节点中,客户端实例信息可能是直接注册到此节点,
//也可能是其他节点同步过来的。此标志起区分作用,以避免二次注册
private boolean replication;
public EurekaInstanceRegisteredEvent(Object source, InstanceInfo instanceInfo,
int leaseDuration, boolean replication) {
super(source); //事件源对象
this.instanceInfo = instanceInfo;
this.leaseDuration = leaseDuration;
this.replication = replication;
}2.3、服务端可用事件 - EurekaRegistryAvailableEvent
源码:
public class EurekaRegistryAvailableEvent extends ApplicationEvent {
public EurekaRegistryAvailableEvent(EurekaServerConfig eurekaServerConfig) {
super(eurekaServerConfig); //初始化事件源对象
}
}从源码中,我们看到,此事件中,只有一个构造器,初始化了事件源对象。事件源对象和服务器启动事件中初始化的事件源是同一类型,都是EurekaServerConfig。
2.4、客户端续约事件 - EurekaInstanceRenewedEvent
重要部分源码:
//app名称
private String appName;
//服务id
private String serverId;
//客户端实例注册信息
private InstanceInfo instanceInfo;
//是否同步。和客户端注册事件一样,用以解决二次续约问题
private boolean replication;
public EurekaInstanceRenewedEvent(Object source, String appName, String serverId,
InstanceInfo instanceInfo, boolean replication) {
super(source); //初始化事件源
this.appName = appName;
this.serverId = serverId;
this.instanceInfo = instanceInfo;
this.replication = replication;
}2.5、客户端下线事件 - EurekaInstanceCanceledEvent
部分重要源码:
//app名称
private String appName;
//服务id
private String serverId;
//是否同步。表示此客户端实例信息是注册信息,还是从其他节点同步来的
private boolean replication;
public EurekaInstanceCanceledEvent(Object source, String appName, String serverId,
boolean replication) {
super(source); //初始化事件源
this.appName = appName;
this.serverId = serverId;
this.replication = replication;
}3、Eureka Server自动配置
Eureka Server自动配置中有一个很重要的配置类EurekaServerAutoConfiguration,在Eureka Server启动时,需要加载此配置类,以完成一些重要组件的初始化,下面我们看看其源码:
@Configuration(proxyBeanMethods = false)
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
...
}EurekaServerAutoConfiguration类中,标注了五个注解。第一个注解@Configuration是一个spring配置注解;第二个注解@Import是一个导入注解,用以导入EurekaServerInitializerConfiguration配置类;第三个注解@ConditionalOnBean是一个条件注解,含义为条件中的bean存在,才会实例化目标bean;第四个注解@EnableConfigurationProperties是一个使注解@ConfigurationProperties标注的配置类生效的注解;第五个注解@PropertySource用以加载指定的属性资源文件。
我们重点来了解一下第二,第三,第四个注解。
@Import注解导入了一个Eureka Server初始化配置类EurekaServerInitializerConfiguration,我们来看看其源码:
@Configuration
public class EurekaServerInitializerConfiguration
implements ServletContextAware, SmartLifecycle, Ordered {
private static final Log log = LogFactory.getLog(EurekaServerInitializerConfiguration.class);
@Autowired
private EurekaServerConfig eurekaServerConfig;
private ServletContext servletContext;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private EurekaServerBootstrap eurekaServerBootstrap;
private boolean running;
private int order = 1;
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
@Override
public void start() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//TODO: is this class even needed now?
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
log.info("Started Eureka Server");
publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
EurekaServerInitializerConfiguration.this.running = true;
publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
}
catch (Exception ex) {
// Help!
log.error("Could not initialize Eureka servlet context", ex);
}
}
}).start();
}
private EurekaServerConfig getEurekaServerConfig() {
return this.eurekaServerConfig;
}
private void publish(ApplicationEvent event) {
this.applicationContext.publishEvent(event);
}
@Override
public void stop() {
this.running = false;
eurekaServerBootstrap.contextDestroyed(this.servletContext);
}
...
}首先,我们可以看到本类中自动注入了eureka服务器配置类EurekaServerConfig、servlet上下文类ServletContext、应用上下文类ApplicationContext以及eureka服务器启动引导类EurekaServerBootstrap。还有两个最重要的start和stop方法,它们都是通过eureka服务器启动引导类启动或关闭eureka服务器的。在start方法中,我们可以看到其发布了两个eureka事件,分别是服务端可用事件和服务器启动事件,事件源为同一个eureka服务器配置对象eurekaServerConfig。
@ConditionalOnBean注解是一个条件注解,其意为条件bean存在时,才加载目标bean。这里我们看到条件bean的类型为EurekaServerMarkerConfiguration.Marker,我们来看看其源码:
@Configuration
public class EurekaServerMarkerConfiguration {
@Bean
public Marker eurekaServerMarkerBean() {
return new Marker();
}
class Marker {
}
}我们发现,EurekaServerMarkerConfiguration类中声明了一个名为Marker的内部类,没什么具体含义,然后创建此注册内部类的bean到spring容器中。查看注释,我们可以看到这个bean是一个标记,起激活eureka server的作用。从文章刚开始,我们就看了@EnableEurekaServer注解的源码,其有个@Import注解,正是导入类此标记类。联想@EnableEurekaServer注解的使用,我们可以得出结论,在eureka server的主类启动时,@EnableEurekaServer注解被加载,其中@EnableEurekaServer注解被加载时,又要加载标记类EurekaServerMarkerConfiguration,而此类是个配置类,其中Marker类型的bean将被注册到spring容器,也就是说,当EurekaServerInitializerConfiguration配置类加载时,其条件bean已经存在于spring bean容器中,EurekaServerInitializerConfiguration配置类满足加载条件,即可加载自身。
@EnableConfigurationProperties注解表示使被@ConfigurationProperties注解标注的属性类生效的注解,在这里,它有两个值,一个是EurekaDashboardProperties类,其为eureka dashboard的属性类,其中有关于eureka dashboard的相关属性配置;另一个是InstanceRegistryProperties类,其中有关于客户端实例注册的属性配置。
EurekaServerAutoConfiguration类上标注的重要注解介绍完,我们来看看其中注入的重要bean,以及那些需要注册的bean。
EurekaServerAutoConfiguration类中有5个注入的bean
- 第一个被注入的bean为ApplicationInfoManager,其作用是初始化eureka server需要的注册信息以及向其他组件提供接口
- 第二个被注入的bean为EurekaServerConfig接口类,提供一些eureka server的配置信息
- 第三个被注入的bean为EurekaClientConfig接口类,提供了eureka客户端向eureka服务器注册实例需要的配置信息
- 第四个被注入的bean为EurekaClient接口类,提供了获取客户端实例、注册和获取客户端心跳检查处理器的能力
- 第五个被注入的bean为InstanceRegistryProperties属性类,提供客户端实例注册相关属性
EurekaServerAutoConfiguration类中有11个将要注册的bean
- HasFeatures,用以actuator收集关键信息
@Bean
public HasFeatures eurekaServerFeature() {
return HasFeatures.namedFeature("Eureka Server",
EurekaServerAutoConfiguration.class);
}- EurekaServerConfig,提前注册eureka服务器配置bean,我们发现此为在一个内部静态类中优先加载
@Configuration
protected static class EurekaServerConfigBeanConfiguration {
@Bean
@ConditionalOnMissingBean
public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
EurekaServerConfigBean server = new EurekaServerConfigBean();
if (clientConfig.shouldRegisterWithEureka()) {
// Set a sensible default if we are supposed to replicate
server.setRegistrySyncRetries(5);
}
return server;
}
}- EurekaController,eureka dashboard控制器bean
@Bean
@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
public EurekaController eurekaController() {
return new EurekaController(this.applicationInfoManager);
}- ServerCodecs,服务器编解码器,用以读取json或xml类型文件编码解码
@Bean
public ServerCodecs serverCodecs() {
return new CloudServerCodecs(this.eurekaServerConfig);
}- PeerAwareInstanceRegistry,其包为com.netflix.eureka.registry。而其实际注册的bean类型为InstanceRegistry,其包为org.springframework.cloud.netflix.eureka.server。Eureka原为netflix的项目,spring将其集成进springcloud中。
@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry(
ServerCodecs serverCodecs) {
this.eurekaClient.getApplications(); // force initialization
return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
serverCodecs, this.eurekaClient,
this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}- PeerEurekaNodes,eureka管理同辈节点生命周期的帮助类
@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry,
ServerCodecs serverCodecs) {
return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}- EurekaServerContext,eureka服务器上下文
@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,
PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
registry, peerEurekaNodes, this.applicationInfoManager);
}- EurekaServerBootstrap,eureka server启动引导,用以初始化或销毁eureka server上下文,环境,
@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,
EurekaServerContext serverContext) {
return new EurekaServerBootstrap(this.applicationInfoManager,
this.eurekaClientConfig, this.eurekaServerConfig, registry,
serverContext);
}- FilterRegistrationBean,注册jersey过滤器
@Bean
public FilterRegistrationBean jerseyFilterRegistration(
javax.ws.rs.core.Application eurekaJerseyApp)
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new ServletContainer(eurekaJerseyApp));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.setUrlPatterns(
Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));
return bean;
}- Application,注册一个jersey应用
@Bean
public javax.ws.rs.core.Application jerseyApplication(
Environment environment,ResourceLoader resourceLoader) {
...
return rc;
}- FilterRegistrationBean,用以跟踪http请求
@Bean
public FilterRegistrationBean traceFilterRegistration(
@Qualifier("httpTraceFilter") Filter filter) {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(filter);
bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
return bean;
}4、eureka server启动加载流程总结
- eureka server项目主类启动
- SpringApplication类调用run方法,秒表计时开始
- 获取spring application运行时监听器,其中会加载目录META-INF下spring.factories文件中的配置类
- 运行时监听器中设置环境信息
- 使用环境信息创建Banner
- 根据web应用类型(默认为servlet类型),创建基于注解的servlet web上下文AnnotationConfigServletWebServerApplicationContext
- 上下文准备
- 上下文刷新
- 上下文刷新后操作
- 秒表计时结束
- 监听器启动上下文,发布已启动事件ApplicationStartedEvent
- 调用运行程序,spring提供了两种运行程序接口,分别是ApplicationRunner接口和CommandLineRunner接口,以供开发者在此处扩展。