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

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 行代码的情况,registrantexistingLease 的应用实例不是同一个对象。
  • 第 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(...)

原文发布于微信公众号 - 芋道源码(YunaiV)

原文发表时间:2018-06-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏恰童鞋骚年

你必须知道的指针基础-1.预备篇:搭建GCC开发环境

  GCC(GNU Compiler Collection)是一套功能强大、性能优越的编程语言编译器,它是GNU计划的代表作品之一。GCC是Linux平台下最常...

731
来自专栏Gaussic

使用IntelliJ IDEA开发SpringMVC网站(三)数据库配置 顶

访问GitHub下载最新源码:https://github.com/gaussic/SpringMVCDemo

942
来自专栏PhpZendo

Symfony 服务容器入门

本文是依赖注入(Depeendency Injection)系列教程的第 3 篇文章,本系列教程主要讲解如何使用 PHP 实现一个轻量级服务容器,教程包括:

1281
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— 应用实例注册发现(六)之全量获取

本文主要分享 Eureka-Client 向 Eureka-Server 获取全量注册信息的过程。

1790
来自专栏向治洪

android ndk之hello world

前言:Android NDK r7及以上的版本已经集成了Cygwin编译环境,也就是说,我们完全可以抛弃庞大的Cygwin了。 r6及以下版本,也可以抛弃几个G...

2825
来自专栏XAI

SpringMVC+MongoDB+Maven整合(微信回调Oauth授权)

个人小程序。里面是基于百度大脑 腾讯优图做的人脸检测。是关于人工智能的哦。 2017年第一篇自己在工作中的总结文档。土豪可以打赏哦。 https://git.o...

8577
来自专栏芋道源码1024

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

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

1030
来自专栏java思维导图

mybatis-plus思维导图,让mybatis-plus不再难懂

 Mybatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果...

1.3K18
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— 应用实例注册发现(七)之增量获取

本文主要分享 Eureka-Client 向 Eureka-Server 获取增量注册信息的过程。

2050
来自专栏Urahara Blog

Apache Struts2 Remote Code Execution (S2-053)

1603

扫码关注云+社区

领取腾讯云代金券