歡迎光臨
每天分享高質量文章

註冊中心 Eureka 原始碼解析 —— 應用實體註冊發現(八)之改寫狀態

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 


本文主要基於 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
  2public 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> gMap = registry.get(appName);
 12:         Lease 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 overriddenInstanceStatusMap = CacheBuilder
          .newBuilder().initialCapacity(500)
          .expireAfterAccess(1, TimeUnit.HOURS)
          .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, nullnull, isReplication);
           return true;
       }
       return false;
    }
    • 呼叫父類 AbstractInstanceRegistry#deleteStatusOverride(…) 方法,刪除應用實體改寫狀態。

3.1 刪除應用實體改寫狀態

呼叫父類 AbstractInstanceRegistry#deleteStatusOverride(...) 方法,刪除應用實體改寫狀態。實現程式碼如下:

  1@Override
  2public 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> gMap = registry.get(appName);
 13:         Lease 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 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
public interface InstanceStatusOverrideRule {

     /**
     * Match this rule.
     *
     * @param instanceInfo The instance info whose status we care about. 關註狀態的應用實體物件
     * @param existingLease Does the instance have an existing lease already? If so let's consider that. 已存在的租約
     * @param isReplication When overriding consider if we are under a replication mode from other servers. 是否是 Eureka-Server 發起的請求
     * @return A result with whether we matched and what we propose the status to be overriden to.
     */

     StatusOverrideResult apply(final InstanceInfo instanceInfo,
                               final Lease existingLease,
                               boolean isReplication)
;

}

// StatusOverrideResult.java
public class StatusOverrideResult {

    public static StatusOverrideResult NO_MATCH = new StatusOverrideResult(falsenull);

    public static StatusOverrideResult matchingStatus(InstanceInfo.InstanceStatus status) {
        return new StatusOverrideResult(true, status);
    }

    // Does the rule match?
    private final boolean matches;

    // The status computed by the rule.
    private final InstanceInfo.InstanceStatus status;

    private StatusOverrideResult(boolean matches, InstanceInfo.InstanceStatus status) {
        this.matches = matches;
        this.status = status;
    }

    public boolean matches() {
        return matches;
    }

    public InstanceInfo.InstanceStatus status() {
        return status;
    }
}
  • #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 ,複合規則,以第一個匹配成功為準。實現程式碼如下:

// 超過微信限制 50000 字了
  • 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(...) 程式碼如下:

// 超過微信限制 50000 字了
  • 註意,使用的是 instanceInfo 。

4.1.3 OverrideExistsRule

com.netflix.eureka.registry.rule.OverrideExistsRule ,匹配應用實體改寫狀態對映( statusOverrides ) 。實現 #apply(...) 程式碼如下:

// 超過微信限制 50000 字了
  • 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(...) 程式碼如下:

// 超過微信限制 50000 字了
  • 註意,使用的是 existingLease ,並且非 Eureka-Server 請求。

4.1.5 AlwaysMatchInstanceStatusRule

com.netflix.eureka.registry.rule.AlwaysMatchInstanceStatusRule ,總是匹配關註狀態的實體物件instanceInfo )的狀態。實現 #apply(...) 程式碼如下:

// 超過微信限制 50000 字了
  • 註意,使用的是 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
// 超過微信限制 50000 字了
  • 第 7 行 :獲得已存在的租約( existingLease ) 。

  • 第 15 行 :建立新的租約( lease )。

  • 第 24 至 28 行 :設定應用實體的改寫狀態( overridestatus ),避免註冊應用實體後,丟失改寫狀態。

  • 第 30 至 32 行 :獲得應用實體最終狀態。註意下,不考慮第 9 行程式碼的情況,registrant 和 existingLease 的應用實體不是同一個物件。

  • 第 33 只 34 行 :設定應用實體的狀態。

4.3 續租場景

// AbstractInstanceRegistry.java
// 超過微信限制 50000 字了
  • 第 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
// 超過微信限制 50000 字了

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 篇++

贊(0)

分享創造快樂