三,springmvc面试知识点

文章详细介绍了SpringCloud中EurekaServer服务注册表的拉取过程,涉及DiscoveryClient的初始化、定时任务的设置,以及EurekaHttpClient的创建,展现了服务注册与发现的核心逻辑。
摘要由CSDN通过智能技术生成

十一.SpringCloud源码剖析-Eureka Server服务注册表拉取

十二.SpringCloud源码剖析-Eureka Server服务剔除

十三.SpringCloud源码剖析-Eureka Server服务下线

前言

文章过长,您需要有些耐心!!!

在上一章《Eureka Client 初始化过程》中我们了解到,应用程序在启动的时候就会初始化Eureka并触发Eureka的自动注册,最终会调用DiscoveryClient进行服务注册,我们来跟踪一下DiscoveryClient是如何实现服务注册与发现的。

1.DiscoveryClient 初始化定时任务

程序启动EurekaClientAutoConfiguration被加载,EurekaClientEurekaClientAutoConfiguration 中通过“延迟@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开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

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)]

  • 22
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值