Spring Cloud Eureka注册中心的工作原理是怎样的?

2026-05-21 03:353阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1457个文字,预计阅读时间需要6分钟。

Spring Cloud Eureka注册中心的工作原理是怎样的?

前言:作为Eureka Client的应用启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会执行以下几件事情:

- 周期性更新服务列表;- 周期性更新服务续约;- 服务注册逻辑;- 概览;- 以下图示:

前言

作为Eureka Client的应用启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会做以下几件事:

  • 周期性更新服务列表;
  • 周期性服务续约;
  • 服务注册逻辑;

概览

以下图片来自Netflix官方,图中显示Eureka Client会发起Register请求将自身注册到注册中心,这样其他Eureka client通过Get Registry请求就能获取到新注册应用的相关信息:

Eureka-Client注册服务

Eureka-Client在两种情况下客户端会主动向服务端发送自己的注册信息

  • 1、当客户端的instance信息发生改变时,Eureka-Client和Server端信息不一致时。
  • 2、当客户端刚刚启动的时候。

源码分析

com.netflix.discovery.DiscoveryClient ,使用的@Inject //google guice 注入遵循 JSR-330规范。

 

首先回顾com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法,Eureka client在启动的时侯都会执行此方法,如下方所示,已经略去了周期性更新服务列表相关的代码:

@Singleton public class DiscoveryClient implements EurekaClient { private void initScheduledTasks() { //省略, 刷新缓存的定时器 //来自EurekaClientConfigBean,默认为true if (clientConfig.shouldRegisterWithEureka()) { //续租间隔 int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); //周期性任务处理超时后,下一次执行时将超时事件翻倍,但是不可超过expBackOffBound的设定范围 int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); //指定时间后启动周期性续租的任务 heartbeatTask = new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ); scheduler.schedule( heartbeatTask, renewalIntervalInSecs, TimeUnit.SECONDS); //上报自身信息到Eureka server的操作委托给InstanceInfoReplicator实例发起, //如果有多个场景需要上报,都由InstanceInfoReplicator进行调度和安排, //并且还有限流逻辑,避免频繁先服务端请求 instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize //监听和响应应用状态变化,包括从停止状态恢复或者进入停止状态, statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } //将自身状态上报都Eureka server(有限流逻辑避免频繁上报) instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { //注册状态变化的监听 applicationInfoManager.registerStatusChangeListener(statusChangeListener); } //更新信息并注册到Eureka server instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } } }

从上述代码可以看出,主动更新和状态变化触发的更新,都委托给成员变量instanceInfoReplicator执行,InstanceInfoReplicator是个辅助类。

 

initScheduledTasks() 方法是在DiscoverClient的构造函数初始化的时候被调用。 主要作用就是:

  • 1、开启缓刷新定时器
  • 2、开启发送心跳的定时器
  • 3、开启实例instance状态变更监听
  • 4、开启应用状态复制器(主要就是为了开启一个定时线程,每40秒判断实例信息是否变更,如果变更了则重新注册)
class InstanceInfoReplicator implements Runnable { public void start(int initialDelayMs) { if (started.compareAndSet(false, true)) { // 首次进来设置一下。 instanceInfo.setIsDirty(); // for initial register // 开启定时线程,每停顿initialDelayMs秒执行一次该任务。服务注册也是由这个任务完成 Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } } // 这个方法主要是在上面提到的监听器里面被调用。 public boolean onDemandUpdate() { if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) { if (!scheduler.isShutdown()) { scheduler.submit(new Runnable() { @Override public void run() { logger.debug("Executing on-demand update of local InstanceInfo"); // 这个地方用来获取定时线程的执行Future, // 如果该线程还没有执行完毕,则取消掉,释放资源,因为下面也会执行run方法 Future latestPeriodic = scheduledPeriodicRef.get(); if (latestPeriodic != null && !latestPeriodic.isDone()) { logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update"); latestPeriodic.cancel(false); } InstanceInfoReplicator.this.run(); } }); return true; } else { logger.warn("Ignoring onDemand update due to stopped scheduler"); return false; } } else { logger.warn("Ignoring onDemand update due to rate limiter"); return false; } } public void run() { try { // 刷新实例信息。 discoveryClient.refreshInstanceInfo(); // 判断实例信息是否不一致 Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { // 注册自己的服务 discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } } catch (Throwable t) { logger.warn("There was a problem with the instance info replicator", t); } finally { Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } } }

从上面可以看到InstanceInfoReplicator是一个负责服务注册的线程任务, 有两个地方可以执行这个任务

  • 1、定时线程,每40秒执行一次。
  • 2、当instance的状态发生变更(除去DOWN这个状态)的时候,会有statusChangeListener 这个监听器监听到去执行服务注册。

注意:

  • 上面finally代码块中,注册完成后又会提交一个一次性的延时任务,这就相当于周期性的执行run方法了,这么一来岂不是会周期性注册?
  • 其实并不会,在执行discoveryClient.register()方法之前有个判断条件的:if (dirtyTimestamp != null),只要成员变量instanceInfo的isDirtyWithTime方法返回为空,就不会执行注册;

先看代码discoveryClient.refreshInstanceInfo(),弄清楚即将上报到Eureka server的信息是如何更新的,如下代码所示,信息更新的操作是委托给ApplicationInfoManager实例来完成的:

@Singleton public class DiscoveryClient implements EurekaClient { void refreshInstanceInfo() { //更新数据 applicationInfoManager.refreshDataCenterInfoIfRequired(); //如果续租时间有变化就要及时更新 applicationInfoManager.refreshLeaseInfoIfRequired(); InstanceStatus status; try { status = getHealthCheckHandler().getStatus(instanceInfo.getStatus()); } catch (Exception e) { logger.warn("Exception from healthcheckHandler.getStatus, setting status to DOWN", e); //如果获取状态异常,就设置当前状态为DOWN status = InstanceStatus.DOWN; } if (null != status) { applicationInfoManager.setInstanceStatus(status); } } }

DiscoveryClient.register()

接下来看看服务注册相关的代码,也就是DiscoveryClient类的register方法,如下所示,源码注释中说到是注册请求类型是Restful的,Eureka server的返回码如果是204表示注册成功,然而在前面的discoveryClient.register()方法内,其实并不关注这个返回值:

@Singleton public class DiscoveryClient implements EurekaClient { /** * Register with the eureka service by making the appropriate REST call. */ boolean register() throws Throwable { logger.info(PREFIX + "{}: registering service...", appPathIdentifier); EurekaHttpResponse<Void> xinchen.blog.csdn.net/article/details/82861618

www.jianshu.com/p/99621c342d06

www.cnblogs.com/zooking/p/13795257.html

Spring Cloud Eureka注册中心的工作原理是怎样的?

本文共计1457个文字,预计阅读时间需要6分钟。

Spring Cloud Eureka注册中心的工作原理是怎样的?

前言:作为Eureka Client的应用启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会执行以下几件事情:

- 周期性更新服务列表;- 周期性更新服务续约;- 服务注册逻辑;- 概览;- 以下图示:

前言

作为Eureka Client的应用启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会做以下几件事:

  • 周期性更新服务列表;
  • 周期性服务续约;
  • 服务注册逻辑;

概览

以下图片来自Netflix官方,图中显示Eureka Client会发起Register请求将自身注册到注册中心,这样其他Eureka client通过Get Registry请求就能获取到新注册应用的相关信息:

Eureka-Client注册服务

Eureka-Client在两种情况下客户端会主动向服务端发送自己的注册信息

  • 1、当客户端的instance信息发生改变时,Eureka-Client和Server端信息不一致时。
  • 2、当客户端刚刚启动的时候。

源码分析

com.netflix.discovery.DiscoveryClient ,使用的@Inject //google guice 注入遵循 JSR-330规范。

 

首先回顾com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法,Eureka client在启动的时侯都会执行此方法,如下方所示,已经略去了周期性更新服务列表相关的代码:

@Singleton public class DiscoveryClient implements EurekaClient { private void initScheduledTasks() { //省略, 刷新缓存的定时器 //来自EurekaClientConfigBean,默认为true if (clientConfig.shouldRegisterWithEureka()) { //续租间隔 int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); //周期性任务处理超时后,下一次执行时将超时事件翻倍,但是不可超过expBackOffBound的设定范围 int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs); //指定时间后启动周期性续租的任务 heartbeatTask = new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ); scheduler.schedule( heartbeatTask, renewalIntervalInSecs, TimeUnit.SECONDS); //上报自身信息到Eureka server的操作委托给InstanceInfoReplicator实例发起, //如果有多个场景需要上报,都由InstanceInfoReplicator进行调度和安排, //并且还有限流逻辑,避免频繁先服务端请求 instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize //监听和响应应用状态变化,包括从停止状态恢复或者进入停止状态, statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { // log at warn level if DOWN was involved logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } //将自身状态上报都Eureka server(有限流逻辑避免频繁上报) instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { //注册状态变化的监听 applicationInfoManager.registerStatusChangeListener(statusChangeListener); } //更新信息并注册到Eureka server instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } } }

从上述代码可以看出,主动更新和状态变化触发的更新,都委托给成员变量instanceInfoReplicator执行,InstanceInfoReplicator是个辅助类。

 

initScheduledTasks() 方法是在DiscoverClient的构造函数初始化的时候被调用。 主要作用就是:

  • 1、开启缓刷新定时器
  • 2、开启发送心跳的定时器
  • 3、开启实例instance状态变更监听
  • 4、开启应用状态复制器(主要就是为了开启一个定时线程,每40秒判断实例信息是否变更,如果变更了则重新注册)
class InstanceInfoReplicator implements Runnable { public void start(int initialDelayMs) { if (started.compareAndSet(false, true)) { // 首次进来设置一下。 instanceInfo.setIsDirty(); // for initial register // 开启定时线程,每停顿initialDelayMs秒执行一次该任务。服务注册也是由这个任务完成 Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } } // 这个方法主要是在上面提到的监听器里面被调用。 public boolean onDemandUpdate() { if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) { if (!scheduler.isShutdown()) { scheduler.submit(new Runnable() { @Override public void run() { logger.debug("Executing on-demand update of local InstanceInfo"); // 这个地方用来获取定时线程的执行Future, // 如果该线程还没有执行完毕,则取消掉,释放资源,因为下面也会执行run方法 Future latestPeriodic = scheduledPeriodicRef.get(); if (latestPeriodic != null && !latestPeriodic.isDone()) { logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update"); latestPeriodic.cancel(false); } InstanceInfoReplicator.this.run(); } }); return true; } else { logger.warn("Ignoring onDemand update due to stopped scheduler"); return false; } } else { logger.warn("Ignoring onDemand update due to rate limiter"); return false; } } public void run() { try { // 刷新实例信息。 discoveryClient.refreshInstanceInfo(); // 判断实例信息是否不一致 Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { // 注册自己的服务 discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } } catch (Throwable t) { logger.warn("There was a problem with the instance info replicator", t); } finally { Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } } }

从上面可以看到InstanceInfoReplicator是一个负责服务注册的线程任务, 有两个地方可以执行这个任务

  • 1、定时线程,每40秒执行一次。
  • 2、当instance的状态发生变更(除去DOWN这个状态)的时候,会有statusChangeListener 这个监听器监听到去执行服务注册。

注意:

  • 上面finally代码块中,注册完成后又会提交一个一次性的延时任务,这就相当于周期性的执行run方法了,这么一来岂不是会周期性注册?
  • 其实并不会,在执行discoveryClient.register()方法之前有个判断条件的:if (dirtyTimestamp != null),只要成员变量instanceInfo的isDirtyWithTime方法返回为空,就不会执行注册;

先看代码discoveryClient.refreshInstanceInfo(),弄清楚即将上报到Eureka server的信息是如何更新的,如下代码所示,信息更新的操作是委托给ApplicationInfoManager实例来完成的:

@Singleton public class DiscoveryClient implements EurekaClient { void refreshInstanceInfo() { //更新数据 applicationInfoManager.refreshDataCenterInfoIfRequired(); //如果续租时间有变化就要及时更新 applicationInfoManager.refreshLeaseInfoIfRequired(); InstanceStatus status; try { status = getHealthCheckHandler().getStatus(instanceInfo.getStatus()); } catch (Exception e) { logger.warn("Exception from healthcheckHandler.getStatus, setting status to DOWN", e); //如果获取状态异常,就设置当前状态为DOWN status = InstanceStatus.DOWN; } if (null != status) { applicationInfoManager.setInstanceStatus(status); } } }

DiscoveryClient.register()

接下来看看服务注册相关的代码,也就是DiscoveryClient类的register方法,如下所示,源码注释中说到是注册请求类型是Restful的,Eureka server的返回码如果是204表示注册成功,然而在前面的discoveryClient.register()方法内,其实并不关注这个返回值:

@Singleton public class DiscoveryClient implements EurekaClient { /** * Register with the eureka service by making the appropriate REST call. */ boolean register() throws Throwable { logger.info(PREFIX + "{}: registering service...", appPathIdentifier); EurekaHttpResponse<Void> xinchen.blog.csdn.net/article/details/82861618

www.jianshu.com/p/99621c342d06

www.cnblogs.com/zooking/p/13795257.html

Spring Cloud Eureka注册中心的工作原理是怎样的?