Eureka 系列(03)Spring Cloud 自动装配原理
[TOC]
本文主要是分析 Spring Cloud 是如何整合 Eureka 的,但不会具体分析 Eureka 的源码,之后的文章会对 Eureka 的源码做一个比较具体的分析。
1. Eureka Client 自动装配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
图1:Eureka Client 自动装配时序图
sequenceDiagram
participant EurekaClientAutoConfiguration
participant EurekaDiscoveryClientConfiguration
participant CloudEurekaClient
participant EurekaAutoServiceRegistration
EurekaClientAutoConfiguration ->> CloudEurekaClient : @Bean
EurekaDiscoveryClientConfiguration ->> EurekaAutoServiceRegistration : @Bean
EurekaAutoServiceRegistration ->> CloudEurekaClient : register
总结: Eureka Client 的装配很简单,主要是组装 DiscoveryClient。
EurekaClientAutoConfiguration
主要是装配 EurekaClientEurekaDiscoveryClientConfiguration
主要是在 Eureka Client 启动时立即将自身注册到 Eureka Server 上。
1.1 装配 DiscoveryClient
EurekaClientAutoConfiguration
主要是装配 CloudEurekaClient,CloudEurekaClient 继承了 DiscoveryClient,主要是增加了 Spring 的事件机制。
@Bean(destroyMethod = "shutdown")
public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config, EurekaInstanceConfig instance,
@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(
appManager, config, this.optionalArgs, this.context);
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
return cloudEurekaClient;
}
1.2 启动时立即注册
EurekaDiscoveryClientConfiguration
主要是实现了自动注册。在 DiscoveryClient 中默认是启动 40s 后才会注册,延迟太长,Spring Cloud 改变了这种默认实现,在启动时调用 EurekaAutoServiceRegistration.start()
,将自身实例注册到 Eureka Server 上。
// EurekaAutoServiceRegistration 启动时自动注册
public void start() {
if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
// 启动时自动注册
this.serviceRegistry.register(this.registration);
this.context.publishEvent(new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig()));
this.running.set(true);
}
}
1.3 替换 EurekaHttpClient
Spring Cloud 实现了自己的 RestTemplateEurekaHttpClient,可以替换默认的 JerseyApplicationClient。DiscoveryClientOptionalArgsConfiguration 中装配条件如下:
@Bean
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
return new RestTemplateDiscoveryClientOptionalArgs();
}
查一下 com.sun.jersey 的依赖情况,可以看到 eureka-client 默认会引入 jersey-client,也就是说会使用默认的 JerseyApplicationClient。
>mvn dependency:tree -Dincludes="com.sun.jersey"
[INFO] com.github.binarylei.springcloud:user-consumer-eureka-client:jar:1.0.0
[INFO] \- org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:jar:2.1.1.RELEASE:compile
[INFO] \- com.netflix.eureka:eureka-client:jar:1.9.8:compile
[INFO] +- com.sun.jersey:jersey-core:jar:1.19.1:runtime
[INFO] \- com.sun.jersey:jersey-client:jar:1.19.1:runtime
既然知道了原因,要替换为 RestTemplateEurekaHttpClient 就很简单了。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<exclusions>
<exclusion>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
</exclusion>
</exclusions>
</dependency>
分析了客户自动装配后,接下来继续分析 Eureka Server 服务端的启动原理,Eureka 服务端的启动同样依赖 DiscoverClient(个人感觉依赖有点混乱)。
2. Eureka Server 自动装配
在原生的 Eureka 中服务端的启动类是 EurekaBootStrap,Spring Cloud 中启动类是 EurekaServerBootstrap,原理大致都差不多。本文会以 EurekaServerBootstrap 为切入点进行分析。
EurekaServerAutoConfiguration 是 Spring Cloud 自动装配入口,配置如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration= org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
注意: EurekaServerAutoConfiguration 需要配合 @EnableEurekaServer 使用。
2.1 Eureka Server 自动装配流程
图2:Eureka Server 自动装配时序图
sequenceDiagram
participant EurekaServerAutoConfiguration
participant EurekaServerInitializerConfiguration
participant PeerAwareInstanceRegistry
participant PeerEurekaNodes
participant EurekaServerBootstrap
participant EurekaClient
EurekaServerAutoConfiguration ->> PeerAwareInstanceRegistry : @Bean
EurekaServerAutoConfiguration ->> PeerEurekaNodes : @Bean
EurekaServerAutoConfiguration ->> EurekaServerBootstrap : @Bean
EurekaServerAutoConfiguration ->> EurekaServerInitializerConfiguration : @Import
loop Eureka Server 服务启动
EurekaServerInitializerConfiguration ->> EurekaServerInitializerConfiguration : start
EurekaServerInitializerConfiguration ->> EurekaServerBootstrap : contextInitialized
EurekaServerBootstrap ->> PeerAwareInstanceRegistry : 1. 从其它服务器同步数据:syncUp
PeerAwareInstanceRegistry ->> EurekaClient : 获取其它服务器的数据:getApplications
EurekaServerBootstrap ->> PeerAwareInstanceRegistry : 2. 启动自动过期定时任务:openForTraffic
end
loop Eureka Server 服务销毁
EurekaServerInitializerConfiguration ->> EurekaServerInitializerConfiguration : stop
EurekaServerInitializerConfiguration ->> EurekaServerBootstrap : contextDestroyed
end
总结: EurekaServerAutoConfiguration 主要是装配 EurekaServerBootstrap 并初始化,主要完成两件事:一是从其它 Eureka Server 上同步数据;二是启动自动过期定时任务 EvictionTask。可以看到 Eureka 最核心的数据结构是 PeerAwareInstanceRegistry。
PeerAwareInstanceRegistry
负责 Eureka Server 之间数据同步,其父类 AbstractInstanceRegistry 则管理所有的本地注册信息。PeerEurekaNodes
负责 Eureka Server 服务器列表管理。EurekaServerBootstrap
启动类。EurekaClient
上文提到 Eureka Server 启动是要依赖 Eureka Client 客户端,所以也会自动装配 EurekaClient,启动时同步数据会依赖 EurekaClient。
2.2 EurekaServerBootstrap
EurekaServerInitializerConfiguration 实现了 SmartLifecycle 接口,也就是启动和销毁时会分别调用 start 和 stop 方法。
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();
}
public void stop() {
this.running = false;
eurekaServerBootstrap.contextDestroyed(this.servletContext);
}
继续关注 EurekaServerBootstrap 的 contextInitialized 和 contextDestroyed 方法分别完成了什么事情。
public void contextInitialized(ServletContext context) {
initEurekaEnvironment();
initEurekaServerContext();
}
protected void initEurekaServerContext() throws Exception {
...
// Copy registry from neighboring eureka node
int registryCount = this.registry.syncUp();
this.registry.openForTraffic(this.applicationInfoManager, registryCount);
// Register all monitoring statistics.
EurekaMonitors.registerAllStats();
}
总结: 可以看到 EurekaServerBootstrap 启动时主要步骤:一是同步数据;二是启动定时过期的任务 EvictionTask。具体的源码会在之后分析。
每天用心记录一点点。内容也许不重要,但习惯很重要!
原文地址:https://www.cnblogs.com/binarylei/p/11614882.html