注册中心 Eureka 源码解析 —— 应用实例注册发现(八)之覆盖状态

eureka 源码分析三--InstanceInfo 中OverriddenStatus的作用 1. eureka server端实例存储结构 ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> 其中key = appName,value = ConcurrentHashMap //存储覆盖状态 protected final ConcurrentMap<String, Inst... 阅读详情

点击上方“芋道源码”,选择“置顶公众号”

技术文章第一时间送达!

源码精品专栏

 

本文主要基于 Eureka 1.8.X 版本

  • 1. 概述

  • 2. 应用实例覆盖状态变更接口

    • 2.1 更新应用实例覆盖状态

  • 3. 应用实例覆盖状态删除接口

    • 3.1 删除应用实例覆盖状态

  • 4. 应用实例覆盖状态映射

    • 4.1 应用实例状态覆盖规则

    • 4.2 注册场景

    • 4.3 续租场景

    • 4.4 下线场景

    • 4.5 过期场景

  • 5. 客户端调用接口

  • 666. 彩蛋


1. 概述

本文主要分享 应用实例的覆盖状态属性。

这里要注意下,不是应用实例的状态( status ),而是覆盖状态( overridestatus ) 。代码如下:

public class InstanceInfo {

    private volatile InstanceStatus overriddenstatus = InstanceStatus.UNKNOWN;

    // ... 省略属性和方法

}

调用 Eureka-Server HTTP Restful 接口 apps/${APP_NAME}/${INSTANCE_ID}/status 对应用实例覆盖状态的变更,从而达到主动的、强制的变更应用实例状态。注意,实际不会真的修改 Eureka-Client 应用实例的状态,而是修改在 Eureka-Server 注册的应用实例的状态。

通过这样的方式,Eureka-Client 在获取到注册信息时,并且配置 eureka.shouldFilterOnlyUpInstances = true,过滤掉非 InstanceStatus.UP 的应用实例,从而避免调动该实例,以达到应用实例的暂停服务( InstanceStatus.OUT_OF_SERVICE ),而无需关闭应用实例。

因此,大多数情况下,调用该接口的目的,将应用实例状态在 ( InstanceStatus.UP ) 和 ( InstanceStatus.OUT_OF_SERVICE ) 之间切换。引用官方代码上的注释如下:

AbstractInstanceRegistry#statusUpdate 方法注释 
Updates the status of an instance. 
Normally happens to put an instance between {@link InstanceStatus#OUT_OF_SERVICE} and {@link InstanceStatus#UP} to put the instance in and out of traffic.


推荐 Spring Cloud 书籍:

  • 请支持正版。下载盗版,等于主动编写低级 BUG 。

  • 程序猿DD —— 《Spring Cloud微服务实战》

  • 周立 —— 《Spring Cloud与Docker微服务架构实战》

  • 两书齐买,京东包邮。

推荐 Spring Cloud 视频:

  • Java 微服务实践 - Spring Boot

  • Java 微服务实践 - Spring Cloud

  • Java 微服务实践 - Spring Boot / Spring Cloud


接口 apps/${APP_NAME}/${INSTANCE_ID}/status 实际是两个:

  • PUT apps/${APP_NAME}/${INSTANCE_ID}/status

  • DELETE apps/${APP_NAME}/${INSTANCE_ID}/status

下面,我们逐节分享这两接口的代码实现。

2. 应用实例覆盖状态变更接口

应用实例覆盖状态变更接口,映射 InstanceResource#statusUpdate() 方法,实现代码如下:

@PUT
@Path("status")
public Response statusUpdate(
       @QueryParam("value") String newStatus,
       @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
       @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
   try {
       // 应用实例不存在
       if (registry.getInstanceByAppAndId(app.getName(), id) == null) {
           logger.warn("Instance not found: {}/{}", app.getName(), id);
           return Response.status(Status.NOT_FOUND).build();
       }

       // 覆盖状态更新
       boolean isSuccess = registry.statusUpdate(app.getName(), id,
               InstanceStatus.valueOf(newStatus), lastDirtyTimestamp,
               "true".equals(isReplication));

       // 返回结果
       if (isSuccess) {
           logger.info("Status updated: " + app.getName() + " - " + id
                   + " - " + newStatus);
           return Response.ok().build();
       } else {
           logger.warn("Unable to update status: " + app.getName() + " - "
                   + id + " - " + newStatus);
           return Response.serverError().build();
       }
   } catch (Throwable e) {
       logger.error("Error updating instance {} for status {}", id,
               newStatus);
       return Response.serverError().build();
   }
}
  • 调用 PeerAwareInstanceRegistryImpl#statusUpdate(...) 方法,更新应用实例覆盖状态。实现代码如下:

    @Override
    public boolean statusUpdate(final String appName, final String id,
                               final InstanceStatus newStatus, String lastDirtyTimestamp,
                               final boolean isReplication) {
       if (super.statusUpdate(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {
           // Eureka-Server 集群同步
           replicateToPeers(Action.StatusUpdate, appName, id, null, newStatus, isReplication);
           return true;
       }
       return false;
    }
    • 调用父类 AbstractInstanceRegistry#statusUpdate(...) 方法,更新应用实例覆盖状态。

2.1 更新应用实例覆盖状态

调用 AbstractInstanceRegistry#statusUpdate(...) 方法,更新应用实例覆盖状态,实现代码如下:

  1: @Override
  2: public boolean statusUpdate(String appName, String id,
  3:                             InstanceStatus newStatus, String lastDirtyTimestamp,
  4:                             boolean isReplication) {
  5:     try {
  6:         // 获取读锁
  7:         read.lock();
  8:         // 添加 覆盖状态变更次数 到 监控
  9:         STATUS_UPDATE.increment(isReplication);
 10:         // 获得 租约
 11:         Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
 12:         Lease<InstanceInfo> lease = null;
 13:         if (gMap != null) {
 14:             lease = gMap.get(id);
 15:         }
 16:         // 租约不存在
 17:         if (lease == null) {
 18:             return false;
 19:         } else {
 20:             // 设置 租约最后更新时间(续租)
 21:             lease.renew();
 22: 
 23:             // 应用实例信息不存在( 防御型编程 )
 24:             InstanceInfo info = lease.getHolder();
 25:             // Lease is always created with its instance info object.
 26:             // This log statement is provided as a safeguard, in case this invariant is violated.
 27:             if (info == null) {
 28:                 logger.error("Found Lease without a holder for instance id {}", id);
 29:             }
 30:             //
 31:             if ((info != null) && !(info.getStatus().equals(newStatus))) {
 32:                 // 设置 租约的开始服务的时间戳(只有第一次有效)
 33:                 // Mark service as UP if needed
 34:                 if (InstanceStatus.UP.equals(newStatus)) {
 35:                     lease.serviceUp();
 36:                 }
 37:                 // 添加到 应用实例覆盖状态映射
 38:                 // This is NAC overridden status
 39:                 overriddenInstanceStatusMap.put(id, newStatus);
 40:                 // 设置 应用实例覆盖状态
 41:                 // Set it for transfer of overridden status to replica on
 42:                 // replica start up
 43:                 info.setOverriddenStatus(newStatus);
 44:                 // 设置 应用实例信息 数据不一致时间
 45:                 long replicaDirtyTimestamp = 0;
 46:                 // 设置 应用实例状态
 47:                 info.setStatusWithoutDirty(newStatus);
 48:                 if (lastDirtyTimestamp != null) {
 49:                     replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp);
 50:                 }
 51:                 // If the replication's dirty timestamp is more than the existing one, just update
 52:                 // it to the replica's.
 53:                 if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) {
 54:                     info.setLastDirtyTimestamp(replicaDirtyTimestamp);
 55:                 }
 56:                 // 添加到 最近租约变更记录队列
 57:                 info.setActionType(ActionType.MODIFIED);
 58:                 recentlyChangedQueue.add(new RecentlyChangedItem(lease));
 59:                 // 设置 最后更新时间
 60:                 info.setLastUpdatedTimestamp();
 61:                 // 设置 响应缓存 过期
 62:                 invalidateCache(appName, info.getVIPAddress(), info.getSecureVipAddress());
 63:             }
 64:             return true;
 65:         }
 66:     } finally {
 67:         // 释放锁
 68:         read.unlock();
 69:     }
 70: }
  • 第 6 至 7 行 :获取读锁。在 《Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁》详细解析。

  • 第 8 至 9 行 :添加覆盖状态变更次数到监控。配合 Netflix Servo 实现监控信息采集。

  • 第 10 至 15 行 :获得租约。

  • 第 16 至 18 行 :租约不存在,返回更新失败。

  • 第 20 至 21 行 :设置租约最后更新时间( 续租 )。

  • 第 23 至 29 行 :持有租约的应用实例不存在,理论来说不会出现,防御性编程。

  • 第 31 行 :应用实例当前状态和覆该状态不一致时才更新覆盖状态。

  • 第 32 至 36 行 :当覆盖状态是 InstanceStatus.UP,设置租约的开始服务的时间戳(只有第一次有效)。

  • 第 37 至 39 行 :添加到应用实例覆盖状态映射( overriddenInstanceStatusMap )。此处英文 "NAC" 可能是 "Network Access Control" 的缩写,感兴趣的可以看看 《Network Access Control》 。overriddenInstanceStatusMap 属性代码如下:

    /**
    * 应用实例覆盖状态映射
    * key:应用实例编号
    */
    protected final ConcurrentMap<String, InstanceStatus> overriddenInstanceStatusMap = CacheBuilder
          .newBuilder().initialCapacity(500)
          .expireAfterAccess(1, TimeUnit.HOURS)
          .<String, InstanceStatus>build().asMap();
    • 有效期 1 小时。每次访问后会刷新有效期,在后文你会看到对其的访问。

  • 第 40 至 43 行 :设置应用实例的覆盖状态。用于 Eureka-Server 集群同步。

  • 第 46 至 47 行 :设置应用实例状态。设置后,Eureka-Client 拉取注册信息,被更新覆盖状态的应用实例就是设置的状态。

  • 第 48 至 55 行 :设置应用实例的数据不一致时间。用于 Eureka-Server 集群同步。

  • 第 56 至 58 行 :添加应用实例到最近租约变更记录队列。

  • 第 59 至 60 行 :设置应用实例的最后更新时间( lastUpdatedTimestamp )。lastUpdatedTimestamp 主要用于记录最后更新时间,无实际业务用途。

  • 第 61 至 62 行 :设置响应缓存过期。

  • 第 64 行 :返回更新成功。

  • 第 68 行 :释放读锁。

3. 应用实例覆盖状态删除接口

当我们不需要应用实例的覆盖状态时,调度接口接口进行删除。关联官方 issue#89 :Provide an API to remove all overridden status。

应用实例覆盖状态删除接口,映射 InstanceResource#deleteStatusUpdate() 方法,实现代码如下:

@DELETE
@Path("status")
public Response deleteStatusUpdate(
       @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
       @QueryParam("value") String newStatusValue,
       @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
   try {
       // 应用实例不存在
       if (registry.getInstanceByAppAndId(app.getName(), id) == null) {
           logger.warn("Instance not found: {}/{}", app.getName(), id);
           return Response.status(Status.NOT_FOUND).build();
       }

       // 覆盖状态删除
       InstanceStatus newStatus = newStatusValue == null ? InstanceStatus.UNKNOWN : InstanceStatus.valueOf(newStatusValue);
       boolean isSuccess = registry.deleteStatusOverride(app.getName(), id,
               newStatus, lastDirtyTimestamp, "true".equals(isReplication));

       // 返回结果
       if (isSuccess) {
           logger.info("Status override removed: " + app.getName() + " - " + id);
           return Response.ok().build();
       } else {
           logger.warn("Unable to remove status override: " + app.getName() + " - " + id);
           return Response.serverError().build();
       }
   } catch (Throwable e) {
       logger.error("Error removing instance's {} status override", id);
       return Response.serverError().build();
   }
}
  • 请求参数 newStatusValue ,设置应用实例的状态。大多数情况下,newStatusValue 要和应用实例实际的状态一致,因为该应用实例的 Eureka-Client 不会从 Eureka-Server 拉取到该应用状态 newStatusValue 。另外一种方式,不传递该参数,相当于 UNKNOWN 状态,这样,Eureka-Client 会主动向 Eureka-Server 再次发起注册,具体原因在 [「4.3 续租场景」] 详细解析,更加推荐的方式。

  • 调用父类 AbstractInstanceRegistry#deleteStatusOverride(...) 方法,删除应用实例覆盖状态。实现代码如下:

    @Override
    public boolean deleteStatusOverride(String appName, String id,
                                       InstanceStatus newStatus,
                                       String lastDirtyTimestamp,
                                       boolean isReplication) {
       if (super.deleteStatusOverride(appName, id, newStatus, lastDirtyTimestamp, isReplication)) {
           // Eureka-Server 集群同步
           replicateToPeers(Action.DeleteStatusOverride, appName, id, null, null, isReplication);
           return true;
       }
       return false;
    }
    • 调用父类 AbstractInstanceRegistry#deleteStatusOverride(...) 方法,删除应用实例覆盖状态。

3.1 删除应用实例覆盖状态

调用父类 AbstractInstanceRegistry#deleteStatusOverride(...) 方法,删除应用实例覆盖状态。实现代码如下:

  1: @Override
  2: public boolean deleteStatusOverride(String appName, String id,
  3:                                     InstanceStatus newStatus,
  4:                                     String lastDirtyTimestamp,
  5:                                     boolean isReplication) {
  6:     try {
  7:         // 获取读锁
  8:         read.lock();
  9:         // 添加 覆盖状态删除次数 到 监控
 10:         STATUS_OVERRIDE_DELETE.increment(isReplication);
 11:         // 获得 租约
 12:         Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
 13:         Lease<InstanceInfo> lease = null;
 14:         if (gMap != null) {
 15:             lease = gMap.get(id);
 16:         }
 17:         // 租约不存在
 18:         if (lease == null) {
 19:             return false;
 20:         } else {
 21:             // 设置 租约最后更新时间(续租)
 22:             lease.renew();
 23: 
 24:             // 应用实例信息不存在( 防御型编程 )
 25:             InstanceInfo info = lease.getHolder();
 26:             // Lease is always created with its instance info object.
 27:             // This log statement is provided as a safeguard, in case this invariant is violated.
 28:             if (info == null) {
 29:                 logger.error("Found Lease without a holder for instance id {}", id);
 30:             }
 31: 
 32:             // 移除 应用实例覆盖状态
 33:             InstanceStatus currentOverride = overriddenInstanceStatusMap.remove(id);
 34:             if (currentOverride != null && info != null) {
 35:                 // 设置 应用实例覆盖状态
 36:                 info.setOverriddenStatus(InstanceStatus.UNKNOWN);
 37:                 // 设置 应用实例状态
 38:                 info.setStatusWithoutDirty(newStatus);
 39:                 // 设置 应用实例信息 数据不一致时间
 40:                 long replicaDirtyTimestamp = 0;
 41:                 if (lastDirtyTimestamp != null) {
 42:                     replicaDirtyTimestamp = Long.valueOf(lastDirtyTimestamp);
 43:                 }
 44:                 // If the replication's dirty timestamp is more than the existing one, just update
 45:                 // it to the replica's.
 46:                 if (replicaDirtyTimestamp > info.getLastDirtyTimestamp()) {
 47:                     info.setLastDirtyTimestamp(replicaDirtyTimestamp);
 48:                 }
 49:                 // 添加到 最近租约变更记录队列
 50:                 info.setActionType(ActionType.MODIFIED);
 51:                 recentlyChangedQueue.add(new RecentlyChangedItem(lease));
 52:                 // 设置 最后更新时间
 53:                 info.setLastUpdatedTimestamp();
 54:                 // 设置 响应缓存 过期
 55:                 invalidateCache(appName, info.getVIPAddress(), info.getSecureVipAddress());
 56:             }
 57:             return true;
 58:         }
 59:     } finally {
 60:         // 释放锁
 61:         read.unlock();
 62:     }
 63: }
  • 第 7 至 8 行 :获取读锁。在 《Eureka源码解析 —— 应用实例注册发现 (九)之岁月是把萌萌的读写锁》详细解析。

  • 第 9 至 10 行 :添加覆盖状态删除次数到监控。配合 Netflix Servo 实现监控信息采集。

  • 第 11 至 16 行 :获得租约。

  • 第 17 至 19 行 :租约不存在,返回更新失败。

  • 第 21 至 22 行 :设置租约最后更新时间( 续租 )。

  • 第 24 至 30 行 :持有租约的应用实例不存在,理论来说不会出现,防御性编程。

  • 第 32 至 33 行 :移除出应用实例覆盖状态映射( overriddenInstanceStatusMap )。

  • 第 34 行 :应用实例的覆盖状态存在才设置状态。

  • 第 35 至 36 行 :设置应用实例的覆盖状态为 InstanceStatus.UNKNOWN。用于 Eureka-Server 集群同步。

  • 第 37 至 38 行 :设置应用实例的状态为 newStatus。设置后,Eureka-Client 拉取注册信息,被更新覆盖状态的应用实例就是设置的状态。

  • 第 39 至 48 行 :设置应用实例的数据不一致时间。用于 Eureka-Server 集群同步。

  • 第 49 至 51 行 :添加应用实例到最近租约变更记录队列。

  • 第 52 至 53 行 :设置应用实例的最后更新时间( lastUpdatedTimestamp )。lastUpdatedTimestamp 主要用于记录最后更新时间,无实际业务用途。

  • 第 54 至 55 行 :设置响应缓存过期。

  • 第 57 行 :返回更新成功。

  • 第 61 行 :释放读锁。

4. 应用实例覆盖状态映射

虽然我们在上面代码,使用覆盖状态( overridestatus )设置到应用实例的状态( status ),实际调用 AbstractInstanceRegistry#getOverriddenInstanceStatus(...) 方法,根据应用实例状态覆盖规则( InstanceStatusOverrideRule )进行计算最终应用实例的状态。实现代码如下:

// AbstractInstanceRegistry.java
protected InstanceInfo.InstanceStatus getOverriddenInstanceStatus(InstanceInfo r,
                                                               Lease<InstanceInfo> existingLease,
                                                               boolean isReplication) {
   InstanceStatusOverrideRule rule = getInstanceInfoOverrideRule();
   logger.debug("Processing override status using rule: {}", rule);
   return rule.apply(r, existingLease, isReplication).status();
}

protected abstract InstanceStatusOverrideRule getInstanceInfoOverrideRule();
  • 调用 #getInstanceInfoOverrideRule() 方法,获取应用实例状态覆盖规则( InstanceStatusOverrideRule )。在 PeerAwareInstanceRegistryImpl 里该方法实现代码如下:

    private final InstanceStatusOverrideRule instanceStatusOverrideRule;

    public PeerAwareInstanceRegistryImpl(
                EurekaServerConfig serverConfig,
                EurekaClientConfig clientConfig,
                ServerCodecs serverCodecs,
                EurekaClient eurekaClient
        ) {
        // ... 省略其它方法
    this.instanceStatusOverrideRule = new FirstMatchWinsCompositeRule(
        new DownOrStartingRule(),
        new OverrideExistsRule(overriddenInstanceStatusMap), 
        new LeaseExistsRule());
    }

    @Override
    protected InstanceStatusOverrideRule getInstanceInfoOverrideRule() {
       return this.instanceStatusOverrideRule;
    }

4.1 应用实例状态覆盖规则

com.netflix.eureka.registry.rule.InstanceStatusOverrideRule ,应用实例状态覆盖规则接口。接口代码如下:

// InstanceStatusOverrideRule.java
// ... 省略,微信长度限制
  • #apply(...) 方法参数 instanceInfo 代表的是关注状态的应用实例,和方法参数 existingLease 里的应用实例不一定是同一个,在 「4.1.6 总结」 详细解析。

  • com.netflix.eureka.registry.rule.StatusOverrideResult ,状态覆盖结果。当匹配成功,返回 matches = true ;否则,返回 matches = false 。

实现类关系如下:


  • AsgEnabledRule ,亚马逊 AWS 专用,跳过。

4.1.1 FirstMatchWinsCompositeRule

com.netflix.eureka.registry.rule.FirstMatchWinsCompositeRule ,复合规则,以第一个匹配成功为准。实现代码如下:

// ... 省略,微信长度限制
  • rules 属性,复合规则集合。在 PeerAwareInstanceRegistryImpl 里,我们可以看到该属性为 [ DownOrStartingRule , OverrideExistsRule , LeaseExistsRule ] 。

  • defaultRule 属性,默认规则,值为 AlwaysMatchInstanceStatusRule 。

  • #apply() 方法,优先使用复合规则( rules ),顺序匹配,直到匹配成功 。当未匹配成功,使用默认规则( defaultRule ) 。

4.1.2 DownOrStartingRule

com.netflix.eureka.registry.rule.DownOrStartingRule ,匹配 InstanceInfo.InstanceStatus.DOWN 或者 InstanceInfo.InstanceStatus.STARTING 状态。实现 #apply(...) 代码如下:

// ... 省略,微信长度限制
  • 注意,使用的是 instanceInfo 。

4.1.3 OverrideExistsRule

com.netflix.eureka.registry.rule.OverrideExistsRule ,匹配应用实例覆盖状态映射( statusOverrides ) 。实现 #apply(...) 代码如下:

// ... 省略,微信长度限制
  • statusOverrides 属性,应用实例覆盖状态映射。在 PeerAwareInstanceRegistryImpl 里,使用 AbstractInstanceRegistry.overriddenInstanceStatusMap 属性赋值。

  • 上文我们提到 AbstractInstanceRegistry.overriddenInstanceStatusMap 每次访问刷新有效期,如果调用到 OverrideExistsRule ,则会不断刷新。从 DownOrStartingRule 看到,instanceInfo 处于 InstanceInfo.InstanceStatus.DOWN 或者 InstanceInfo.InstanceStatus.STARTING 才不会继续调用 OverrideExistsRule 匹配,AbstractInstanceRegistry.overriddenInstanceStatusMap 才有可能过期。

4.1.4 LeaseExistsRule

com.netflix.eureka.registry.rule.LeaseExistsRule ,匹配已存在租约的应用实例的 nstanceStatus.OUT_OF_SERVICE 或者 InstanceInfo.InstanceStatus.UP 状态。实现 #apply(...) 代码如下:

// ... 省略,微信长度限制
  • 注意,使用的是 existingLease ,并且非 Eureka-Server 请求。

4.1.5 AlwaysMatchInstanceStatusRule

com.netflix.eureka.registry.rule.AlwaysMatchInstanceStatusRule ,总是匹配关注状态的实例对象( instanceInfo )的状态。实现 #apply(...) 代码如下:

// ... 省略,微信长度限制
  • 注意,使用的是 instanceInfo 。

4.1.6 总结

我们将 PeerAwareInstanceRegistryImpl 的应用实例覆盖状态规则梳理如下:


  • 应用实例状态是最重要的属性,没有之一,因而在最终实例状态的计算,以可信赖为主。

  • DownOrStartingRule ,instanceInfo 处于 STARTING 或者 DOWN 状态,应用实例可能不适合提供服务( 被请求 ),考虑可信赖,返回 instanceInfo 的状态。

  • OverrideExistsRule ,当存在覆盖状态( statusoverrides ) ,使用该状态,比较好理解。

  • LeaseExistsRule ,来自 Eureka-Client 的请求( 非 Eureka-Server 集群请求),当 Eureka-Server 的实例状态存在,并且处于 UP 或则 OUT_OF_SERVICE ,保留当前状态。原因,禁止 Eureka-Client 主动在这两个状态之间切换。如果要切换,使用应用实例覆盖状态变更与删除接口。

  • AlwaysMatchInstanceStatusRule ,使用 instanceInfo 的状态返回,以保证能匹配到状态。

  • 在下文中,你会看到,#getOverriddenInstanceStatus() 方法会在注册和续租使用到。结合上图,我们在 「4.2 注册场景」 和 「4.3 续租场景」 也会详细解析。

  • 在下文中,你会看到,#getOverriddenInstanceStatus() 方法会在注册和续租使用到,方法参数 instanceInfo 情况如下:

    • 注册时 :请求参数 instanceInfo ,和 existingLease 的应用实例属性不相等( 如果考虑 Eureka-Server 的 LastDirtyTimestamp 更大的情况,则类似 续租时的情况 ) 。

    • 续租时 :使用 Eureka-Server 的 existingLease 的应用实例,两者相等。

    • 总的来说,可以将 instanceInfo 理解成请求方的状态。

  • DownOrStartingRule ,

4.2 注册场景

// AbstractInstanceRegistry.java
// ... 省略,微信长度限制
  • 第 7 行 :获得已存在的租约( existingLease ) 。

  • 第 15 行 :创建新的租约( lease )。

  • 第 24 至 28 行 :设置应用实例的覆盖状态( overridestatus ),避免注册应用实例后,丢失覆盖状态。

  • 第 30 至 32 行 :获得应用实例最终状态。注意下,不考虑第 9 行代码的情况,registrant 和 existingLease 的应用实例不是同一个对象。

  • 第 33 只 34 行 :设置应用实例的状态。

4.3 续租场景

// AbstractInstanceRegistry.java
// ... 省略,微信长度限制
  • 第 15 至 17 行 :获得应用实例的最终状态。

  • 第 18 至 24 行 :应用实例的最终状态为 UNKNOWN,无法续约 。返回 false 后,请求方( Eureka-Client 或者 Eureka-Server 集群其他节点 )会发起注册,在 《Eureka 源码解析 —— 应用实例注册发现(二)之续租》 有详细解析。为什么会是 UNKNOWN 呢?在 「3. 应用实例覆盖状态删除接口」 传递应用实例状态为 UNKNOWN 。

  • 第 25 至 36 行 :应用实例的状态与最终状态不相等,使用最终状态覆盖应用实例的状态。为什么会不相等呢?#renew(...) 和 #statusUpdate(...) 可以无锁,并行执行,如果

    • #renew(...) 执行完第 16 行代码,获取到 overriddenInstanceStatus 后,恰巧 #statusUpdate(...) 执行完更新应用实例状态 newStatus,又恰好两者不相等,使用 overriddenInstanceStatus 覆盖掉应用实例的 newStatus 状态。

    • 那岂不是覆盖状态( overriddenstatus )反倒被覆盖???不会,在下一次心跳,应用实例的状态会被修正回来。当然,如果应用实例状态如果为 UP 或者 STARTING 不会被修正,也不应该被修正。

4.4 下线场景

// AbstractInstanceRegistry.java
protected boolean internalCancel(String appName, String id, boolean isReplication) {

    // ... 省略无关代码

    // 移除 应用实例覆盖状态映射
    InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
    if (instanceStatus != null) {
        logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
    }

}

4.5 过期场景

同 「4.4 下线场景」 相同。

5. 客户端调用接口

对应用实例覆盖状态的变更和删除接口调用,点击如下方法查看,非常易懂,本文就不啰嗦了:

  • AbstractJerseyEurekaHttpClient#statusUpdate(...)

  • AbstractJerseyEurekaHttpClient#deleteStatusOverride(...)

666. 彩蛋

猜测覆盖状态的花费了较长时间,梳理应用实例覆盖规则耗费大量脑细胞。

下一篇,让我鸡鸡动动的,Eureka-Server 集群同步走起!

胖友,分享我的公众号( 芋道源码 ) 给你的胖友可好?


知识星球

目前在知识星球(https://t.zsxq.com/2VbiaEu)更新了如下 Dubbo 源码解析如下:

01. 调试环境搭建
02. 项目结构一览
03. API 配置(一)之应用
04. API 配置(二)之服务提供者
05. API 配置(三)之服务消费者
06. 属性配置
07. XML 配置
08. 核心流程一览

09. 拓展机制 SPI

10. 线程池

11. 服务暴露(一)之远程暴露(Injvm)

12. 服务暴露(二)之远程暴露(Dubbo)

...
一共 60 篇++

Spring Cloud 入门 -- 搭建Eureka注册中心 实现服务者与消费者的服务调用_eureka 注册中心调用服务 摘自Spring Cloud官网Spring Cloud 为开发者提供了工具来快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话,集群状态)。分布式系统的协调导致了样板模式,使用 Spring Cloud 开发人员可以快速建立实现这些模式的服务和应用程序。它们在任何分布式环境中都能很好地工作,包括开发人员自己的笔记本电脑、裸机数据中心以及 Cloud Foundry 等托管平台。大致意思就是。 阅读详情

相关推荐

微服务架构:注册中心 Eureka、ZooKeeper、Consul、Nacos的选型对比详解

微服务架构:注册中心 Eureka、ZooKeeper、Consul、Nacos的选型对比详解

永远保持随时离开的技术能力! 3024

注册中心—注册中心原理

在微服务架构中,注册中心是最核心的基础服务之一,本文将详细介绍下注册中心的组成部分和它们之前的关系。 目录 一、注册中心原理 二、注册中心功能 三、常见的注册中心 一、注册中心原理 注册中心主要涉及到三大角色:服务提供者、服务消费者、注册中心,它们之间的关系大致如下: 各个微服务在启动时,将自己的网络地址等信息注册到注册中心,注册中心存储这些数据。 服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。 各个微服务与注册中心使用一定机制(例如心跳)通信。如果注册中.

sanmi8276的博客 8375

Eureka 服务注册源码探秘——图解、源码级解析

文章目录关于注册中心Eureka 服务注册源码 关于注册中心 服务注册是为了解决“你是谁”这个问题,即获取所有服务节点的身份信息和服务名称,从注册中心的角度来说我们有以下两种比较直观的解决方案: 由注册中心主动访问网络节点中所有机器 注册中心等待服务节点上门注册 目前主流的注册中心都选择第二种方案,主要原因是第一种方案有很多弊端: 模型复杂: 网络环境浩如烟海,轮询每个节点的做法通常是注册中心发局域网广播,客户端响应的方式。现实中对于跨局域网的分布式系统来说,响应模型更加复杂 网络消耗大: 整个网络环

如切如磋,如琢如磨 5531

Spring Cloud 常见面试题总结(二)

Part4 【Q-01】Eureka Client 提交的 CANCEL_OVERRIDE 状态修改请求,Eureka Server 是如何处理的,Server 端都做了哪些变更?请谈一下你的认识。 【RA】Eureka Client 提交的 CANCEL_OVERRIDE 状态修改请求,Eureka Server 在接收到后,首先根据该 Client 的微服务名称及 instanceId 在 Server 端注册表中进行了查找。若没有找到,则直接返回 404;若找到了,其会执行两大任务: 任务一:将 S

语雀同名:犬豪 yuque.com/qhao 474

【Spring Cloud实战:Eureka】服务注册与发现

Spring Cloud Eureka 服务注册与发现 Spring Cloud Eureka是Spring Cloud Netflix 子项目的核心组件之一,主要用于微服务架构中的服务治理。 文章针对 搭建Eureka注册中心,搭建Eureka客户端,搭建Eureka集群及给Eureka注册中心添加登录认证进行介绍和实操。 Eureka简介 在微服务架构中往往会有一个注册中心,每个微服务都会向注册中心去注册自己的地址及端口信息,注册中心维护着服务名称与服务实例的对应关系。 每个微服务都会定时从注册中心获取

Strive_Peter的博客 2832

Emotion-LLaMA实战指南:从零构建多模态情感识别系统(附Python代码)

本文提供了Emotion-LLaMA多模态情感识别系统的实战指南。通过详细的步骤,指导读者从零搭建开发环境、运行本地Demo、理解模型架构,并完成数据处理与模型训练。该创新模型融合视觉、听觉与文本信息,实现更精准、接近人类理解的情感分析与推理,并附有完整的Python代码实现。

weixin_29094775的博客 418

Eureka核⼼源码剖析

目录 一. Eureka Server启动过程 观察类头分析 三个关注点 (一)关注点1 (二)关注点2 (三)关注点3 二. Eureka Server服务接⼝暴露策略 三. Eureka Server服务注册接⼝(接受客户端注册服务) 四. Eureka Server服务续约接⼝(接受客户端续约) 五. Eureka Client注册服务 (一)读取配置⽂件 (二)启动时从EurekaServer获取服务实例信息 (三)注册⾃⼰到EurekaServer (四)开启⼀..

舞鹤白沙编码日志 402

用RFSoC ZU47DR搭建软件无线电原型:从零配置Vivado到生成第一个射频信号

本文详细介绍了如何使用Xilinx RFSoC ZU47DR开发板搭建软件无线电原型系统,从Vivado环境配置到生成首个射频信号的全流程。通过整合ARM处理器、FPGA逻辑和高速ADC/DAC,该平台为通信算法验证提供了高效解决方案,特别适合SDR开发者和通信工程师快速实现原型设计。

weixin_26763955的博客 592

Eureka 源码解析 —— 应用实例注册发现(八)之覆盖状态

摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-override-status/ 「芋道源码」欢迎转载,保留摘要,谢谢!本文主要基于 Eureka 1.8.X 版本 1. 概述 2. 应用实例覆盖状态变更接口 2.1 更新应用实例覆盖状态3. 应用实例覆盖状态删除接口 3.1 删除应用实例覆盖状态 4. 应用实例覆盖状态映射 4

天黑了 854

一文详解心电图(ECG)监测原理

介绍心电图的监测原理,从心电信号产生、获取到心电图的基本组成

m0_50146396的博客 3301

下载Python3.10的相关方法及问题解决

下载Python3.10的相关方法及问题解决 报错unable to locate package python3.10-dev以及subprocess.CalledProcessError: Command ‘(‘lsb_release‘, ‘-a‘)‘ returned non-zero exit status 1问题解决

Istil 1555

注册中心 Eureka 源码解析 —— 应用实例注册发现(一)之注册

摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-register/ 「芋道源码」欢迎转载,保留摘要,谢谢! 本文主要基于 Eureka 1.8.X 版本 1. 概述 2. Eureka-Client 发起注册 2.1 应用实例信息复制器 2.2 刷新应用实例信息 2.3 发起注册应用实例 3. Eureka-Server 接...

芋艿V 948

【LAMMPS学习】八、基础知识(4.5)TIP5P水模型

​ 五点 TIP5P 刚性水模型通过添加两个通常无质量的附加位点 L 来扩展three-point TIP3P model,其中放置与氧原子相关的电荷。这些位点 L 位于距氧原子固定距离处,形成从 HOH 平面旋转 90 度的四面角。因此,这些位点在某种程度上近似于氧的孤对,从而改善了水结构,与four-point TIP4P model相比变得更加“四面体”。

荷塘阅色 1421

SpringCloud服务注册与发现:Eureka架构与实现原理

SpringCloud Eureka作为微服务架构中的核心组件,通过服务注册与发现机制解决了服务通信的难题。本文从Eureka的基础架构入手,详细介绍了其注册机制、高可用设计、自我保护机制和服务发现功能,并通过源码分析深入理解了Eureka的实现原理。与其他注册中心相比,Eureka在可用性方面表现出色,特别适合对服务可用性要求较高的分布式系统。在实际应用中,开发者需要根据业务需求和系统特性选择合适的注册中心。随着微服务架构的普及,熟练掌握Eureka等服务注册与发现组件已成为Java开发者的必备技能。

程序媛学姐的博客 1992

Android AMS——AMS初始化(二)

Android AMS 也是一个系统服务,属于系统服务中的引导服务,在 startBootstrapServices() 方法中启动。这里我们就来看一下 ActivityManagerService 的启动流程。

小旭的专栏 779

Open3D——直通滤波(python详细过程版)【2025最新版】

python就一个字:强。博客长期更新,本文最近更新时间为:2025年1月11日。

点云侠的博客 7519

Eureka注册中心

Eureka 是 Netflix 开发的一款服务发现(Service Discovery)工具,它是 Spring Cloud Netflix 微服务套件的一部分。服务发现是微服务架构中的关键组件之一,允许服务实例在启动时向 Eureka 服务器注册自己的信息(如IP地址和端口),并定期发送心跳来更新状态。

m0_62943934的博客 1477

[关系图谱] 二.Gephi导入共线矩阵构建作者关系图谱

本文主要讲解Gephi绘制作者间的关系图谱,该软件可以广泛应用于社交网络、知识图谱分析,推荐读者使用。这是非常基础的一篇文章,重点讲解Gephi使用方法,希望对大家有所帮助。

杨秀璋的专栏 1万+

注册中心-Eureka 基础篇

注册中心-Eureka 基础篇 文章目录注册中心-Eureka 基础篇前言项目环境1.什么是注册中心?1.1 服务注册1.2 服务发现1.3 整体流程图2.什么是 Eureka?3.部署 Eureka Server 服务端依赖引导类application.yaml 配置文件4.部署 Eureka Client 客户端依赖引导类application.yaml 配置文件启动效果5.集群部署6.总结 前言 注册中心的内容分为上下两章进行发布 上篇,基础部分,介绍注册中心的基本概念和含义,以及 Eureka 的

谢文峰的博客 2988

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【微服务】Nacos 注册中心的设计原理

注册中心的核心数据是服务的名字和它对应的网络地址,当服务注册了多个实例时,我们需要对不健康的实例进行过滤或者针对实例的⼀些特征进行流量的分配,那么就需要在实例上存储⼀些例如健康状态、权重等属性。

qq_57756904的博客 9817

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Eureka 源码解析 —— Eureka源码解析 —— 应用实例注册发现 (九)之读写锁

1. 概述 本文主要分享Eureka 注册中心的那把读写锁,让我瘙痒难耐,却不得其解。在某次意外的抠脚的一刻( 笔者不抽烟,如果抽烟的话,此处应该就不是抠脚了 ),突然顿悟,爽,这好比... 比喻有点猥琐,笔者就省略 100 字。 不瞎比比,上代码: public abstract class AbstractInstanceRegistry implements InstanceRegistry { private final ReentrantReadWriteLock readWr.

weixin_42073629的博客 470

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Spring Cloud 入门 -- 搭建Eureka注册中心 实现服务者与消费者的服务调用

基于Spring Cloud 搭建Eureka注册中心,详细入门教程~

wanghuichen的博客 3285

注册中心的基础知识

定义:注册中心是一个集中的服务目录,它允许应用程序注册和发现服务。它充当了服务提供者和服务消费者之间的中介。服务注册:服务提供者将自己的服务注册到注册中心中,以便服务消费者可以发现并使用它们。服务发现:服务消费者从注册中心中获取服务提供者的信息,并与之建立通信。服务路由:注册中心可以根据负载均衡策略将请求路由到适当的服务提供者。服务监控:注册中心可以监控服务的状态和健康状况,以便及时发现和处理故障。

Flying_Fish_roe的博客 2132

服务注册和发现之Eureka原理篇

概念 在传统应用组件间调用,通过接口规范约束来实现的,从而实现不同模块间良好协作;但是被拆分成微服务后,每个微服务实例的数量和网络地址都可能动态变化,使得原来硬编码的地址极不方便,故需要一个中心化的组件来进行服务的登记和管理。 服务注册中心:实现服务治理,管理所有的服务信息和状态。 注册中心好处:不用关心服务提供方数量、地址等细节。 注册中心技术栈:Eureka、Nacos、Consul、Zookeeper等。 服务注册与发现包括两部分:一个是服务器端,另一个是客户端 Server是一个公共服务,为Clie

weixin_45380450的博客 2078
上一篇: 分布式消息队列 RocketMQ 源码分析 —— Message 拉取与消费(上)
下一篇: 分布式消息队列 RocketMQ 源码分析 —— Message 拉取与消费(下)
公众号-芋道源码
博客等级 码龄10年 1635粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值