Eureka Server启动过程

429 阅读4分钟

一 Eureka Server的启动

1.1 创建Eureka server项目

在Springboot项目中,引入如下依赖;在启动类上使用@EnableEurekaServer,添加简单的配置,就可启动eureka server了。

<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    <version>1.4.0.RELEASE</version>
</dependency>
eureka:
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

我们一起来看看eureka server是如何启动的。

1.2 包依赖关系

spring-cloud-starter-netflix-eureka-server中,引入了spring-cloud-netflix-eureka-server

<dependency>  
    <groupId>org.springframework.cloud</groupId>  
    <artifactId>spring-cloud-netflix-eureka-server</artifactId>  
</dependency>

spring-cloud-netflix-eureka-server中,又引入了eureka-coreeureka-client等。

<dependency>
	<groupId>com.netflix.eureka</groupId>
	<artifactId>eureka-core</artifactId>
</dependency>
<dependency>
	<groupId>com.netflix.eureka</groupId>
	<artifactId>eureka-client</artifactId>
</dependency>

1.3 Springboot下eureka server启动流程

1.3.1 @EnableEurekaServer注解的作用

@EnableEurekaServer,加载了EurekaServerMarkerConfiguration类。 image.png 该类仅仅创建了一个Marker类实例,用于标记启用eureka服务端。 image.png

真正的配置类,是spring.factories指明的EurekaServerAutoConfiguration类。 image.png

看到@ConditionalOnBean,你是不是明白了Marker类的作用:只在容器中存在Marker实例时,才加载配置类。 image.png

1.3.2 EurekaServerAutoConfiguration

EurekaServerAutoConfiguration中创建了EurekaServerBootstrap实例,该类的contextInitialized()方法,完成了启动过程。 image.png 这个方法在哪儿被调用呢?

来看EurekaServerInitializerConfiguration类,通过@Import引入。它实现了Spring的SmartLifecycle接口,在start()方法中调用EurekaServerBootstrap.contextInitialized方法。 image.png 至此Eureka Server启动了。

SmartLifecycle是Lifecycle接口的扩展。LifeCycle定义了Spring容器中bean的生命周期。

ApplicationContext本身接收启动停止信号时,spring容器将在上下文中找出所有实现了LifeCycle及其子接口的类,并一一调用它们的start方法。

二 不使用Springboot时

2.1 EurekaBootStrap类

在eureka-core包中,有一个EurekaBootStrap类,它实现了ServletContextListener接口,复写了contextInitialized方法。 image.png image.png 是不是很眼熟?怎么和springboot中EurekaServerBootstrap类contextInitialized方法,一模一样。

其实,EurekaServerBootstrap只是对eureka源码中EurekaBootStrap类的改写,将原本用new创建的对象,改用@Bean交由spring容器来管理。

2.2 ServletContextListener

ServletContextListener,监听ServletContext对象的创建和销毁, ServletContext对象创建后会调用contextInitialized方法。

在eureka-server包的web.xml中,有如下配置。

<listener>  
    <listener-class>com.netflix.eureka.EurekaBootStrap</listener-class>  
</listener>

可见,源码中eureka server通过servlet-api的ServletContextListener来启动。

三 contextInitialized的逻辑

其实,就干了两件事:初始化环境和初始化上下文

public void contextInitialized(ServletContextEvent event) {
	try {
		initEurekaEnvironment();
		initEurekaServerContext();
		// 省略非核心代码
	}
}

3.1 初始化环境

initEurekaEnvironment中,主要获取配置管理类的一个单例。

String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);

static volatile AbstractConfiguration instance。使用双重检测+volatile实现单例模式。 image.png

3.2 初始化上下文

3.2.1 加载eureka-server配置文件

此时,会创建DefaultEurekaServerConfig。

EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

配置文件是eureka-server.properties,在eureka-server包中。 image.png 可是,文档中内容全被注释了。真正的配置从哪儿来呢? image.png DefaultEurekaServerConfig中有很多get方法,为各配置项提供了默认值。 image.png

evictionIntervalTimerInMs:eureka-server清理无效节点的时间间隔

3.2.2 创建ApplicationInfoManager实例

ApplicationInfoManager类,是服务配置管理器,提供向Eureka Server注册所需的信息。 image.png EurekaInstanceConfig中很多get方法,用于获取服务端配置项。 image.png

在集群部署时,eureka server间可以相互注册;某个eureka server节点,相对于其他节点而言,就是一个客户端。

因此,创建InstanceInfo来封装eureka server实例的服务信息,如ip、port等。

private volatile String instanceId;  
private volatile String appName;
private volatile String ipAddr;
private volatile int port = DEFAULT_PORT;

3.2.3 创建eurekaClient实例

eurekaClient是包含在eureka-server 服务中的,用来跟集群中其他 eureka-server实例进行通信的。 image.png eurekaClient的启动过程,将在随后的《Eureka Client的启动过程》一文中介绍。

3.2.4 创建PeerAwareInstanceRegistry实例

PeerAwareInstanceRegistry接口,定义了Eureka Server各节点间的数据同步方式,使集群保持最终一致性。

image.png

非AWS环境下,会创建PeerAwareInstanceRegistryImpl实例,该类的主要功能,如源码所说:

  • 复制其他节点的主要操作有注册、续订、取消、过期和状态更改
  • 当eureka服务器启动时,它尝试从对等eureka节点获取所有注册表信息。如果由于某种原因此操作失败,服务器不允许用户在EurekaServerConfig.getWaitTimeInMsWhenSyncEmpty()指定的一段时间内获取注册表信息。
  • 自我保护机制:如果在EurekaServerConfig.getRenewalThresholdUpdateIntervalMs()的一段时间内,续订量下降超过EurekaServerConfig.getRenewalPercentThreshold()中指定的阈值,eureka将停止剔除过期实例。

3.2.5 创建上下文实例

即DefaultEurekaServerContext类。 image.png EurekaServerContextHolder是serverContext的持有者,采用单例模式。 image.png

调用serverContext.initialize(),其中逻辑将在随后的文章中进一步介绍。 image.png

3.2.6 从相邻节点拷贝注册表

通过前面创建的eurekaClient,从集群中其他节点拉取注册表,并添加到本地注册表。

// Copy registry from neighboring eureka node  
int registryCount = registry.syncUp();

3.2.7 启动eureka 监控

image.png

四 结论

  1. 在springboot下,提供了自动配置,并通过SmartLifecycle将容器与eureka启动流程相串联;
  2. 在源码中,通过ServletContextListener,在web应用上下文创建后,调用eureka启动流程。
  3. Eureka Server启动时,需要从集群中其他节点同步数据,是通过创建一个eureka client,来拉取注册表信息。
  4. Eureka Server的启动流程,比Eureka client复杂,建议先了解后者。