十一.SpringCloud源码剖析-Eureka Server服务注册表拉取
十二.SpringCloud源码剖析-Eureka Server服务剔除
十三.SpringCloud源码剖析-Eureka Server服务下线
前言
文章过长,您需要有些耐心!!!
在上一章《Eureka Client 初始化过程》中我们了解到,应用程序在启动的时候就会初始化Eureka并触发Eureka的自动注册,最终会调用DiscoveryClient
进行服务注册,我们来跟踪一下DiscoveryClient是如何实现服务注册与发现的。
1.DiscoveryClient 初始化定时任务
程序启动EurekaClientAutoConfiguration
被加载,EurekaClient
在EurekaClientAutoConfiguration
中通过“延迟@Lazy
”注册。同时EurekaAutoServiceRegistration
监听启动事件,调用 EurekaServiceRegistry
的register方法进行注册,该方法会触发EurekaClient的创建 。自动配置类中有如下代码
//可刷新的Eureka客户端配置
@Configuration
@ConditionalOnRefreshScope
protected static class RefreshableEurekaClientConfiguration {
@Autowired
private ApplicationContext context;
@Autowired
private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
//注册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) {
//初始化InstanceInfo,服务实例注册信息对象
manager.getInfo(); // force initialization
//创建CloudEurekaClient ,他是 EurekaClient的实现类
return new CloudEurekaClient(manager, config, this.optionalArgs,
this.context);
}
…省略…
这里初始化InstanceInfo
服务注册实例之后,创建了EurekaClient 客户端,通过子类 CloudEurekaClient
进行创建,只不过这里是@Lazy延迟创建
,在,跟踪下去,我们看一下CloudEurekaClient是如何创建的
public class CloudEurekaClient extends DiscoveryClient {
…省略…
private ApplicationInfoManager applicationInfoManager;
private AtomicReference eurekaHttpClient = new AtomicReference<>();
public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
EurekaClientConfig config, ApplicationEventPublisher publisher) {
this(applicationInfoManager, config, null, publisher);
}
//创建CloudEurekaClient的构造器
public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
EurekaClientConfig config,
AbstractDiscoveryClientOptionalArgs<?> args,
ApplicationEventPublisher publisher) {
//通过父类DiscoveryClient进行初始化
super(applicationInfoManager, config, args);
this.applicationInfoManager = applicationInfoManager;
this.publisher = publisher;
this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, “eurekaTransport”);
ReflectionUtils.makeAccessible(this.eurekaTransportField);
}
这里super(applicationInfoManager, config, args);通过父类初始化他的父类是DiscoveryClient,DiscoveryClient的父接口是EurekaClient
我们继续跟踪上去DiscoveryClient的构造器
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider backupRegistryProvider) {
if (args != null) {
//健康检查处理器
this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
//健康检查回调
this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
this.eventListeners.addAll(args.getEventListeners());
this.preRegistrationHandler = args.preRegistrationHandler;
} else {
this.healthCheckCallbackProvider = null;
this.healthCheckHandlerProvider = null;
this.preRegistrationHandler = null;
}
//InstanceInfo管理器
this.applicationInfoManager = applicationInfoManager;
//InstanceInfo服务实例注册信息,注册的对象
InstanceInfo myInfo = applicationInfoManager.getInfo();
//eureka客户端配置
clientConfig = config;
staticClientConfig = clientConfig;
//和eurekaServrer的通信配置,该配置通过EurekaClientConfigBean来创建
transportConfig = config.getTransportConfig();
instanceInfo = myInfo;
if (myInfo != null) {
appPathIdentifier = instanceInfo.getAppName() + “/” + instanceInfo.getId();
} else {
logger.warn(“Setting instanceInfo to a passed in null value”);
}
//备份注册表
this.backupRegistryProvider = backupRegistryProvider;
this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo);
localRegionApps.set(new Applications());
fetchRegistryGeneration = new AtomicLong(0);
remoteRegionsToFetch = new AtomicReference(clientConfig.fetchRegistryForRemoteRegions());
remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(“,”));
//注册表过时监控
if (config.shouldFetchRegistry()) {
this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + “lastUpdateSec_”, new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}
//心跳监控
if (config.shouldRegisterWithEureka()) {
this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + “lastHeartbeatSec_”, new long[]{15L, 30L, 60L, 120L, 240L, 480L});
} else {
this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC;
}
logger.info(“Initializing Eureka in region {}”, clientConfig.getRegion());
//如果不注册,不拉取注册列表,置空相关的定时任务以及相关配置
if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) {
logger.info(“Client configured to neither register nor query for data.”);
scheduler = null;
heartbeatExecutor = null;
cacheRefreshExecutor = null;
eurekaTransport = null;
instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion());
// This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()
// to work with DI’d DiscoveryClient
DiscoveryManager.getInstance().setDiscoveryClient(this);
DiscoveryManager.getInstance().setEurekaClientConfig(config);
initTimestampMs = System.currentTimeMillis();
logger.info(“Discovery Client initialized at timestamp {} with initial instances count: {}”,
initTimestampMs, this.getApplications().size());
return; // no need to setup up an network tasks and we are done
}
try {
//初始化线程池======================================
//创建定时任务执行器,核心数2
// 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(),
new ThreadFactoryBuilder()
.setNameFormat(“DiscoveryClient-HeartbeatExecutor-%d”)
.setDaemon(true)
.build()
); // use direct handoff
//刷新服务列表缓存线程执行器
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue(),
new ThreadFactoryBuilder()
.setNameFormat(“DiscoveryClient-CacheRefreshExecutor-%d”)
.setDaemon(true)
.build()
); // use direct handoff
//初始化和EurekaServer交互的客户端EurekaHttpClient,
//会创建服务注册的EurekaHttpClient和拉取注册表的EurekaHttpClient
eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);
AzToRegionMapper azToRegionMapper;
if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig);
} else {
azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig);
}
if (null != remoteRegionsToFetch.get()) {
azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(“,”));
}
instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, clientConfig.getRegion());
} catch (Throwable e) {
throw new RuntimeException(“Failed to initialize DiscoveryClient!”, e);
}
//从来备份中拉取注册表,底层没做实现
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
fetchRegistryFromBackup();
}
// call and execute the pre registration handler before all background tasks (inc registration) is started
if (this.preRegistrationHandler != null) {
this.preRegistrationHandler.beforeRegistration();
}
//如果开启服务注册
if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
try {
//调用register()方法发起服务注册 ,默认 clientConfig.shouldEnforceRegistrationAtInit是不满足的
if (!register() ) {
throw new IllegalStateException(“Registration error at startup. Invalid server response.”);
}
} catch (Throwable th) {
logger.error(“Registration error at startup: {}”, th.getMessage());
throw new IllegalStateException(th);
}
}
//初始化定时任务
// finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
initScheduledTasks();
try {
//监视注册表
Monitors.registerObject(this);
} catch (Throwable e) {
logger.warn(“Cannot register timers”, e);
}
// This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()
// to work with DI’d DiscoveryClient
DiscoveryManager.getInstance().setDiscoveryClient(this);
DiscoveryManager.getInstance().setEurekaClientConfig(config);
initTimestampMs = System.currentTimeMillis();
logger.info(“Discovery Client initialized at timestamp {} with initial instances count: {}”,
initTimestampMs, this.getApplications().size());
}
构造器中做了一些初始化工作
-
比如初始化心跳线程执行器,服务列表刷新线程执行器
-
比如创建
EurekaTransport
,用来和Eureka交互的客户端,内部创建了EurekaHttpClient
来发请求 -
最后调用
initScheduledTasks
方法进行定时任务的初始化
2.initScheduledTasks初始化定时任务
接下来我们详细看一下initScheduledTasks
中的代码:
/**
初始化所有定时任务
- Initializes all scheduled tasks.
*/
private void initScheduledTasks() {
//判断如果要拉取注册表
if (clientConfig.shouldFetchRegistry()) {
//刷新注册表的心跳时间间隔
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
//刷新注册表的定时任务:CacheRefreshThread刷新注册表
scheduler.schedule(
new TimedSupervisorTask(
“cacheRefresh”,
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
//是否要注册到Eureaka
if (clientConfig.shouldRegisterWithEureka()) {
//这里取的是租约更新时间 30s/次
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + “renew interval is: {}”, renewalIntervalInSecs);
// Heartbeat timer
//心跳续约的定时任务 :HeartbeatThread 中进行续约,内部调用DiscoverClient.renew方法
scheduler.schedule(
new TimedSupervisorTask(
“heartbeat”,
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
ew TimedSupervisorTask(
“heartbeat”,
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-75wum9nN-1710912464103)]
[外链图片转存中…(img-ayd9VAoV-1710912464104)]
[外链图片转存中…(img-Fn7j5mfp-1710912464104)]
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-JoTlzNUl-1710912464105)]