我们从下面的图可以看出,作为一个client端,他有以下功能:
- Register(服务注册):向Eureka Server注册自身的信息,比如IP地址、端口等。
- Renew(服务续约):服务启动后,每隔30秒会向Eureka Server发送心跳进行服务续约。
- Get(服务发现):获取向Eureka Server注册的服务列表,并缓存本地,默认30秒。
- Cancel(服务下线):向Eureka Server发送下线信息,Eureka Server会把这个服务标志为下线。
服务启动
springboot的自动装配读取Eureka Client的spring.factories文件,我们看到他里面有多个EnableAutoConfiguration,主要的类是EurekaClientAutoConfiguration。EurekaClientAutoConfiguration类中有多个bean的加载,我们先看看EurekaInstanceConfigBean的加载。
EurekaInstanceConfigBean
EurekaInstanceConfigBean有个@ConfigurationProperties("eureka.instance")注解,所以我们在配置文件里写的eureka.instance.instanceId这类的信息最终都在赋值给这个类。还有比较重要的leaseRenewalIntervalInSeconds续约频率(默认每30秒)、leaseExpirationDurationInSeconds续约过期时间(默认90秒)都是在这个类配置的。
@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class,
search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
ManagementMetadataProvider managementMetadataProvider) {
// 省略部分代码
EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);
// 省略部分代码
return instance;
}
ApplicationInfoManager
ApplicationInfoManager也是在EurekaClientAutoConfiguration类中。
创建eurekaApplicationInfoManager对象的时候,会先创建InstanceInfo对象,InstanceInfo对象的值大部分是从EurekaInstanceConfig复制过来的,另外租约信息是存放在InstanceInfo的leaseInfo中。
所以初始化ApplicationInfoManager的时候,就是赋值EurekaInstanceConfig和InstanceInfo。
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class,
search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
return new ApplicationInfoManager(config, instanceInfo);
}
EurekaClientConfigBean
EurekaClientConfigBean也是一个配置信息的bean,他主要是EurekaClient和EurekaServer相关的信息,比如EurekaServer的地址defaultZone、是否从注册中心拉取注册信息fetchRegistry(默认true),拉取的频率registryFetchIntervalSeconds(默认每30秒)、是否向注册中心注册registerWithEureka(默认true)等。
@Bean
@ConditionalOnMissingBean(value = EurekaClientConfig.class,
search = SearchStrategy.CURRENT)
public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
return new EurekaClientConfigBean();
}
EurekaClient
创建一个EurekaClient类,这个类就是负责服务的注册、续约、取消、获取注册列表。
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class,
search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public EurekaClient eurekaClient(ApplicationInfoManager manager,
EurekaClientConfig config, EurekaInstanceConfig instance,
@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
ApplicationInfoManager appManager;
if (AopUtils.isAopProxy(manager)) {
appManager = ProxyUtils.getTargetObject(manager);
}
else {
appManager = manager;
}
CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
config, this.optionalArgs, this.context);
cloudEurekaClient.registerHealthCheck(healthCheckHandler);
return cloudEurekaClient;
}
结合上面几个类的加载以及EurekaClient的加载,我们可以知道这几个类的关系如下:
在DiscoveryClient的构造函数里,主要是初始化一些配置参数,以及为拉取注册表信息、初始化定时任务。
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
// 初始化applicationInfoManager、config等略
try {
// 创建核心数为2的线程池,一个用于heartbeat,一个用于cacheRefresh
// default size of 2 - 1 each for heartbeat and cacheRefresh
scheduler = Executors.newScheduledThreadPool(2,
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-%d")
.setDaemon(true)
.build());
heartbeatExecutor = new ThreadPoolExecutor(
1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
// 与EurekaServer网络通信用的
eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);
//其他的略
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
}
if (clientConfig.shouldFetchRegistry()) {
try {
// 拉取注册表信息
boolean primaryFetchRegistryResult = fetchRegistry(false);
//其他的略,没有拉取成功从备份注册中心获取
}
}
//其他的略
// 初始化定时任务
initScheduledTasks();
//其他的略
}
initScheduledTasks,用来开启更新服务注册列表和续租的定时任务,以及对EurekaServer的注册。
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
// 这个定时任务是更新服务注册列表
// 更新频率
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
// 延迟最大乘数,比如更新频率是5s,那最大延迟时间就是50s,这个后面讲线程的时候会具体说
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
cacheRefreshTask = new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
);
scheduler.schedule(
cacheRefreshTask,
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2);
if (clientConfig.shouldRegisterWithEureka()) {
// 这个定时任务是续租
// 续约频率
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);
// Heartbeat timer
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
scheduler.schedule(
heartbeatTask,
renewalIntervalInSecs, TimeUnit.SECONDS);
// 其他的略
// 注册到注册中心
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
} else {
logger.info("Not registering with Eureka server per configuration");
}
}
好了,以上对EurekaServer的注册、续租、注册表获取都提及到了,后面再详细的讲解这些内容。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。